Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From ec81aee9156f43f4 To tkt-b6eea9446d
2015-04-03
| ||
03:56 | Evidently, the changes on this branch are no longer required. The underlying database locking issue has already been resolved. ... (Closed-Leaf check-in: 999de0b7 user: mistachkin tags: tkt-b6eea9446d) | |
03:55 | Update error messages and help text for the 'all' sub-command. ... (check-in: ac56af17 user: mistachkin tags: trunk) | |
03:07 | Further simplifications. ... (check-in: 52641f30 user: mistachkin tags: tkt-b6eea9446d) | |
2013-10-08
| ||
07:23 | Even better error message in case of "manifest file (12892) is malformed". Detection of this situation is not 100% failproof yet, but if zParentUuid=0 we know for sure that an illegal manifest will be generated later, so better catch this early. ... (check-in: 27dc7a56 user: jan.nijtmans tags: trunk) | |
2013-10-07
| ||
22:37 | Add the 'fossil all extra' command. ... (check-in: f4bcc63f user: mistachkin tags: allExtra) | |
20:24 | Initial groundwork for fixing bug [b6eea9446d]. ... (check-in: d5407ff9 user: mistachkin tags: tkt-b6eea9446d) | |
13:27 | Support for tunneling https through http proxy. ... (check-in: c039efde user: jan tags: jan-httpsproxytunnel) | |
08:10 | Prevent an invalid manifest to be generated if there is no parent manifest. ... (check-in: aa01de5d user: jan.nijtmans tags: pending-review) | |
07:41 | Better error message in case of "manifest file (12892) is malformed". It will now give an additional line: "line ???: wrong size UUID on P-card" (or whatever other parsing error happens), and using the "-n" option it will print out the complete manifest as well. This would have made it much easier to investigate Ron Aaron's commit problem (many thanks for reporting this!), without adding special debugging code to fossil. ... (check-in: ec81aee9 user: jan.nijtmans tags: trunk) | |
2013-10-05
| ||
22:34 | Backout check-in [7557c3e03f]. The code in that check-in is not necessary. (It was added only to try to catch errors.) Fix for ticket [9d9d42d9aab]. ... (check-in: 6d1c038c user: drh tags: trunk) | |
2013-10-04
| ||
09:30 | Better error-message in case of "manifest file is malformed" ... (Closed-Leaf check-in: 1eb438d6 user: jan.nijtmans tags: pending-review) | |
Added .dockerignore.
> > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | _FOSSIL_ .fslckout ajax art autosetup bld compat debian fossil fossil.exe setup src test tools win wbld win www *.a *.lib *.log *.manifest *.o *.obj *.pdb *.res |
Added .fossil-settings/encoding-glob.
> > | 1 2 | compat/zlib/contrib/dotzlib/DotZLib/*.cs win/fossil.rc |
Changes to .fossil-settings/ignore-glob.
1 2 3 4 | compat/openssl* compat/tcl* fossil fossil.exe | > | 1 2 3 4 5 | compat/openssl* compat/tcl* fossil fossil.exe win/fossil.exe |
Changes to .fossil-settings/keep-glob.
1 2 3 4 | compat/openssl* compat/tcl* fossil fossil.exe | > | 1 2 3 4 5 | compat/openssl* compat/tcl* fossil fossil.exe win/fossil.exe |
Changes to BUILD.txt.
︙ | ︙ | |||
20 21 22 23 24 25 26 | If you have VC++ installed on your system, then consider: cd win; nmake /f Makefile.msc If you have trouble, or you want to do something fancy, just look at Makefile.classic. There are 6 configuration options that are all well | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | If you have VC++ installed on your system, then consider: cd win; nmake /f Makefile.msc If you have trouble, or you want to do something fancy, just look at Makefile.classic. There are 6 configuration options that are all well commented. Instead of editing the Makefile.classic, consider copying Makefile.classic to an alternative name such as "GNUMakefile", "BSDMakefile", or "makefile" and editing the copy. BUILDING OUTSIDE THE SOURCE TREE An out of source build is pretty easy: |
︙ | ︙ | |||
59 60 61 62 63 64 65 | real makefile in src/main.mk. The src/main.mk makefile is automatically generated by a TCL script found at src/makemake.tcl. Do not edit src/main.mk directly. Update src/makemake.tcl and then rerun it. * The *.h header files are automatically generated using a program called "makeheaders". Source code to the makeheaders program is | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | real makefile in src/main.mk. The src/main.mk makefile is automatically generated by a TCL script found at src/makemake.tcl. Do not edit src/main.mk directly. Update src/makemake.tcl and then rerun it. * The *.h header files are automatically generated using a program called "makeheaders". Source code to the makeheaders program is found in src/makeheaders.c. Documentation is found in src/makeheaders.html. * Most *.c source files are preprocessed using a program called "translate". The sources to translate are found in src/translate.c. A header comment in src/translate.c explains in detail what it does. * The src/mkindex.c program generates some C code that implements static lookup tables. See the header comment in the source code for details on what it does. Additional information on the build process is available from http://www.fossil-scm.org/fossil/doc/trunk/www/makefile.wiki |
Changes to COPYRIGHT-BSD2.txt.
1 2 | Copyright 2007 D. Richard Hipp. All rights reserved. | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | Copyright 2007 D. Richard Hipp. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and contributors and should not be interpreted as representing official policies, either expressed or implied, of anybody else. |
Added Dockerfile.
> > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ### # Dockerfile for Fossil ### FROM fedora:21 ### Now install some additional parts we will need for the build RUN yum update -y && yum install -y gcc make zlib-devel openssl-devel tar && yum clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil ### If you want to build "release", change the next line accordingly. ENV FOSSIL_INSTALL_VERSION trunk RUN curl "http://core.tcl.tk/tcl/tarball/tcl-src.tar.gz?name=tcl-src&uuid=release" | tar zx RUN cd tcl-src/unix && ./configure --prefix=/usr --disable-shared --disable-threads --disable-load && make && make install RUN curl "http://www.fossil-scm.org/index.html/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx RUN cd fossil-src && ./configure --disable-lineedit --disable-fusefs --json --with-th1-docs --with-th1-hooks --with-tcl RUN cd fossil-src && make && strip fossil && cp fossil /usr/bin && cd .. && rm -rf fossil-src && chmod a+rx /usr/bin/fossil && mkdir -p /opt/fossil && chown fossil:fossil /opt/fossil ### Build is done, remove modules no longer needed RUN yum remove -y gcc make zlib-devel openssl-devel tar && yum clean all USER fossil ENV HOME /opt/fossil EXPOSE 8080 CMD ["/usr/bin/fossil", "server", "--create", "--user", "admin", "/opt/fossil/repository.fossil"] |
Changes to Makefile.classic.
︙ | ︙ | |||
23 24 25 26 27 28 29 | BCC = gcc #### The suffix to add to final executable file. When cross-compiling # to windows, make this ".exe". Otherwise leave it blank. # E = | | > > > > | > | | | | > > > > > | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | BCC = gcc #### The suffix to add to final executable file. When cross-compiling # to windows, make this ".exe". Otherwise leave it blank. # E = #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # #TCC = gcc -O6 #TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage TCC = gcc -g -Os -Wall # To use the included miniz library # FOSSIL_ENABLE_MINIZ = 1 # TCC += -DFOSSIL_ENABLE_MINIZ # To add support for HTTPS TCC += -DFOSSIL_ENABLE_SSL #### Extra arguments for linking the finished binary. Fossil needs # to link against the Z-Lib compression library unless the miniz # library in the source tree is being used. There are no other # required dependencies. We sometimes add the -static option # here so that we can build a static executable that will run in # a chroot jail. # ZLIB_LIB.0 = -lz ZLIB_LIB.1 = ZLIB_LIB. = $(ZLIB_LIB.0) # If using zlib: LIB = $(ZLIB_LIB.$(FOSSIL_ENABLE_MINIZ)) $(LDFLAGS) # If using HTTPS: LIB += -lcrypto -lssl #### Tcl shell for use in running the fossil testsuite. If you do not # care about testing the end result, this can be blank. # |
︙ | ︙ |
Changes to Makefile.in.
︙ | ︙ | |||
35 36 37 38 39 40 41 | #### Tcl shell for use in running the fossil testsuite. If you do not # care about testing the end result, this can be blank. # TCLSH = tclsh LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ | | | < < | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #### Tcl shell for use in running the fossil testsuite. If you do not # care about testing the end result, this can be blank. # TCLSH = tclsh LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H INSTALLDIR = $(DESTDIR)@prefix@/bin USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@ include $(SRCDIR)/main.mk distclean: clean rm -f autoconfig.h config.log Makefile |
Changes to VERSION.
|
| | | 1 | 1.32 |
Changes to auto.def.
1 2 3 4 5 6 | # System autoconfiguration. Try: ./configure --help use cc cc-lib options { with-openssl:path|auto|none | | > > > > | | | | | | | > > > > > > > > > > > > > > > < | < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | # System autoconfiguration. Try: ./configure --help use cc cc-lib options { with-openssl:path|auto|none => {Look for OpenSSL in the given path, or auto or none} with-miniz=0 => {Use miniz from the source tree} with-zlib:path => {Look for zlib in the given path} with-th1-docs=0 => {Enable TH1 for embedded documentation pages} with-th1-hooks=0 => {Enable TH1 hooks for commands and web pages} with-tcl:path => {Enable Tcl integration, with Tcl in the specified path} with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism} with-tcl-private-stubs=0 => {Enable Tcl integration via private stubs mechanism} internal-sqlite=1 => {Don't use the internal SQLite, use the system one} static=0 => {Link a static executable} lineedit=1 => {Disable line editing} fusefs=1 => {Disable the Fuse Filesystem} fossil-debug=0 => {Build with fossil debugging enabled} json=0 => {Build with fossil JSON API enabled} } # sqlite wants these types if possible cc-with {-includes {stdint.h inttypes.h}} { cc-check-types uint32_t uint16_t int16_t uint8_t } # Use pread/pwrite system calls in place of seek + read/write if possible define USE_PREAD [cc-check-functions pread] # Find tclsh for the test suite. Can't yet use jimsh for this. cc-check-progs tclsh define EXTRA_CFLAGS "" define EXTRA_LDFLAGS "" define USE_SYSTEM_SQLITE 0 if {![opt-bool internal-sqlite]} { proc find_internal_sqlite {} { # On some systems (slackware), libsqlite3 requires -ldl to link. So # search for the system SQLite once with -ldl, and once without. If # the library can only be found with $extralibs set to -ldl, then # the code below will append -ldl to LIBS. # foreach extralibs {{} {-ldl}} { # Locate the system SQLite by searching for sqlite3_open(). Then check # if sqlite3_strglob() can be found as well. If we can find open() but # not strglob(), then the system SQLite is too old to link against # fossil. # if {[cc-check-function-in-lib sqlite3_open sqlite3 $extralibs]} { if {![cc-check-function-in-lib sqlite3_strglob sqlite3 $extralibs]} { user-error "system sqlite3 too old (require >= 3.7.17)" } # Success. Update symbols and return. # define USE_SYSTEM_SQLITE 1 define-append LIBS $extralibs return } } user-error "system sqlite3 not found" } find_internal_sqlite } if {[string match *-solaris* [get-define host]]} { define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__} } if {[opt-bool fossil-debug]} { define-append EXTRA_CFLAGS -DFOSSIL_DEBUG msg-result "Debugging support enabled" } if {[opt-bool json]} { # Reminder/FIXME (stephan): FOSSIL_ENABLE_JSON # is required in the CFLAGS because json*.c # have #ifdef guards around the whole file without # reading config.h first. define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON define FOSSIL_ENABLE_JSON msg-result "JSON support enabled" } if {[opt-bool with-th1-docs]} { define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_DOCS define FOSSIL_ENABLE_TH1_DOCS msg-result "TH1 embedded documentation support enabled" } if {[opt-bool with-th1-hooks]} { define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_HOOKS define FOSSIL_ENABLE_TH1_HOOKS msg-result "TH1 hooks support enabled" } #if {[opt-bool markdown]} { # # no-op. Markdown is now enabled by default. # msg-result "Markdown support enabled" #} if {[opt-bool static]} { # XXX: This will not work on all systems. define-append EXTRA_LDFLAGS -static msg-result "Trying to link statically" } set tclpath [opt-val with-tcl] if {$tclpath ne ""} { set tclprivatestubs [opt-bool with-tcl-private-stubs] # Note parse-tclconfig-sh is in autosetup/local.tcl if {$tclpath eq "1"} { |
︙ | ︙ | |||
158 159 160 161 162 163 164 | define-append LIBS $libs } define-append EXTRA_CFLAGS $cflags define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS) define FOSSIL_ENABLE_TCL } | | | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | define-append LIBS $libs } define-append EXTRA_CFLAGS $cflags define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS) define FOSSIL_ENABLE_TCL } # Helper for OpenSSL checking proc check-for-openssl {msg {cflags {}}} { msg-checking "Checking for $msg..." set rc 0 msg-quiet cc-with [list -cflags $cflags -libs {-lssl -lcrypto}] { if {[cc-check-includes openssl/ssl.h] && [cc-check-functions SSL_new]} { incr rc } |
︙ | ︙ | |||
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | define-append EXTRA_CFLAGS -Wdeprecated-declarations } } } else { user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support" } } if {[opt-bool lineedit]} { # Need readline-compatible line editing cc-with {-includes stdio.h} { if {[cc-check-includes readline/readline.h] && [cc-check-function-in-lib readline readline]} { msg-result "Using readline for line editing" } elseif {[cc-check-includes editline/readline.h] && [cc-check-function-in-lib readline edit]} { define-feature editline msg-result "Using editline for line editing" } } } # Network functions require libraries on some systems cc-check-function-in-lib gethostbyname nsl if {![cc-check-function-in-lib socket {socket network}]} { # Last resort, may be Windows if {[string match *mingw* [get-define host]]} { define-append LIBS -lwsock32 } } cc-check-function-in-lib iconv iconv # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars if {![cc-check-functions getpassphrase]} { # Haiku needs this cc-check-function-in-lib getpass bsd } cc-check-function-in-lib dlopen dl make-template Makefile.in make-config-header autoconfig.h -auto {USE_* FOSSIL_*} | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | define-append EXTRA_CFLAGS -Wdeprecated-declarations } } } else { user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support" } } if {[opt-bool with-miniz]} { define FOSSIL_ENABLE_MINIZ 1 msg-result "Using miniz for compression" } else { # Check for zlib, using the given location if specified set zlibpath [opt-val with-zlib] if {$zlibpath ne ""} { cc-with [list -cflags "-I$zlibpath -L$zlibpath"] define-append EXTRA_CFLAGS -I$zlibpath define-append EXTRA_LDFLAGS -L$zlibpath msg-result "Using zlib from $zlibpath" } if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} { user-error "zlib not found please install it or specify the location with --with-zlib" } } if {[opt-bool lineedit]} { # Need readline-compatible line editing cc-with {-includes stdio.h} { if {[cc-check-includes readline/readline.h] && [cc-check-function-in-lib readline readline]} { define-append EXTRA_CFLAGS -DHAVE_READLINE msg-result "Using readline for line editing" } elseif {[cc-check-includes editline/readline.h] && [cc-check-function-in-lib readline edit]} { define-feature editline define-append EXTRA_CFLAGS -DHAVE_EDITLINE msg-result "Using editline for line editing" } } } # Network functions require libraries on some systems cc-check-function-in-lib gethostbyname nsl if {![cc-check-function-in-lib socket {socket network}]} { # Last resort, may be Windows if {[string match *mingw* [get-define host]]} { define-append LIBS -lwsock32 } } cc-check-function-in-lib iconv iconv cc-check-functions utime cc-check-functions usleep cc-check-functions strchrnul # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE if {![cc-check-functions getloadavg]} { define FOSSIL_OMIT_LOAD_AVERAGE 1 msg-result "Load average support unavailable" } # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars if {![cc-check-functions getpassphrase]} { # Haiku needs this cc-check-function-in-lib getpass bsd } cc-check-function-in-lib dlopen dl # Check for the FuseFS library if {[opt-bool fusefs]} { if {[cc-check-function-in-lib fuse_mount fuse]} { define FOSSIL_HAVE_FUSEFS 1 define-append LIBS -lfuse msg-result "FuseFS support enabled" } } make-template Makefile.in make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
Changes to autosetup/autosetup.
︙ | ︙ | |||
923 924 925 926 927 928 929 | } } } # Load module source in the global scope by executing the given command proc automf_load {args} { if {[catch [list uplevel #0 $args] msg opts] ni {0 2 3}} { | | | 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 | } } } # Load module source in the global scope by executing the given command proc automf_load {args} { if {[catch [list uplevel #0 $args] msg opts] ni {0 2 3}} { autosetup-full-error [error-dump $msg $opts $::autosetup(debug)] } } # Initial settings set autosetup(exe) $::argv0 set autosetup(istcl) 1 set autosetup(start) [clock millis] |
︙ | ︙ | |||
1704 1705 1706 1707 1708 1709 1710 1711 | #puts "Skipping $info(file):$info(line)" } return $msg } # Given the return from [catch {...} msg opts], returns an appropriate # error message. A nice one for Jim and a less-nice one for Tcl. # | > > | > | > | | | > > > > > > > > > > > > > | 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 | #puts "Skipping $info(file):$info(line)" } return $msg } # Given the return from [catch {...} msg opts], returns an appropriate # error message. A nice one for Jim and a less-nice one for Tcl. # If 'fulltrace' is set, a full stack trace is provided. # Otherwise a simple message is provided. # # This is designed for developer errors, e.g. in module code or auto.def code # # proc error-dump {msg opts fulltrace} { if {$::autosetup(istcl)} { if {$fulltrace} { return "Error: [dict get $opts -errorinfo]" } else { return "Error: $msg" } } else { lassign $opts(-errorinfo) p f l if {$f ne ""} { set result "$f:$l: Error: " } append result "$msg\n" if {$fulltrace} { append result [stackdump $opts(-errorinfo)] } # Remove the trailing newline string trim $result } } } # ----- module text-formatting ----- set modsource(text-formatting) { |
︙ | ︙ | |||
1884 1885 1886 1887 1888 1889 1890 | ################################################################## # # Entry/Exit # if {$autosetup(debug)} { main $argv } | | | | | 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 | ################################################################## # # Entry/Exit # if {$autosetup(debug)} { main $argv } if {[catch {main $argv} msg opts] == 1} { show-notices autosetup-full-error [error-dump $msg $opts $::autosetup(debug)] if {!$autosetup(debug)} { puts stderr "Try: '[file tail $autosetup(exe)] --debug' for a full stack trace" } exit 1 } |
Changes to autosetup/cc-shared.tcl.
︙ | ︙ | |||
92 93 94 95 96 97 98 99 100 101 102 103 | # XXX: These haven't been tested define SHOBJ_CFLAGS "+O3 +z" define SHOBJ_LDFLAGS -b define SH_CFLAGS +z define SH_LINKFLAGS -Wl,+s define LD_LIBRARY_PATH SHLIB_PATH } } if {![is-defined SHOBJ_LDFLAGS_R]} { define SHOBJ_LDFLAGS_R [get-define SHOBJ_LDFLAGS] } | > > > > > > > > > | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | # XXX: These haven't been tested define SHOBJ_CFLAGS "+O3 +z" define SHOBJ_LDFLAGS -b define SH_CFLAGS +z define SH_LINKFLAGS -Wl,+s define LD_LIBRARY_PATH SHLIB_PATH } *-*-haiku { define SHOBJ_CFLAGS "" define SHOBJ_LDFLAGS -shared define SH_CFLAGS "" define SH_LDFLAGS -shared define SH_LINKFLAGS "" define SH_SOPREFIX "" define LD_LIBRARY_PATH LIBRARY_PATH } } if {![is-defined SHOBJ_LDFLAGS_R]} { define SHOBJ_LDFLAGS_R [get-define SHOBJ_LDFLAGS] } |
Changes to autosetup/cc.tcl.
︙ | ︙ | |||
331 332 333 334 335 336 337 | -declare { lappend new($name) $value } -libs { # Note that new libraries are added before previous libraries set new($name) [list {*}$value {*}$new($name)] } | | | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | -declare { lappend new($name) $value } -libs { # Note that new libraries are added before previous libraries set new($name) [list {*}$value {*}$new($name)] } -link - -lang - -nooutput { set new($name) $value } -source - -sourcefile - -code { # XXX: These probably are only valid directly from cctest set new($name) $value } default { |
︙ | ︙ | |||
426 427 428 429 430 431 432 433 434 435 436 437 438 439 | ## -declare code Code to declare before main() ## -link 1 Don't just compile, link too ## -lang c|c++ Use the C (default) or C++ compiler ## -libs liblist List of libraries to link, e.g. {-ldl -lm} ## -code code Code to compile in the body of main() ## -source code Compile a complete program. Ignore -includes, -declare and -code ## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file] # # Unless -source or -sourcefile is specified, the C program looks like: # ## #include <firstinclude> /* same for remaining includes in the list */ ## ## declare-code /* any code in -declare, verbatim */ ## | > | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | ## -declare code Code to declare before main() ## -link 1 Don't just compile, link too ## -lang c|c++ Use the C (default) or C++ compiler ## -libs liblist List of libraries to link, e.g. {-ldl -lm} ## -code code Code to compile in the body of main() ## -source code Compile a complete program. Ignore -includes, -declare and -code ## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file] ## -nooutput 1 Treat any compiler output (e.g. a warning) as an error # # Unless -source or -sourcefile is specified, the C program looks like: # ## #include <firstinclude> /* same for remaining includes in the list */ ## ## declare-code /* any code in -declare, verbatim */ ## |
︙ | ︙ | |||
519 520 521 522 523 524 525 | } return $ok } writefile $src $lines\n set ok 1 | | > | 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 | } return $ok } writefile $src $lines\n set ok 1 set err [catch {exec-with-stderr {*}$cmdline} result errinfo] if {$err || ($opts(-nooutput) && [string length $result])} { configlog "Failed: [join $cmdline]" configlog $result configlog "============" configlog "The failed code was:" configlog $lines configlog "============" set ok 0 |
︙ | ︙ | |||
669 670 671 672 673 674 675 | if {[get-define CC] eq ""} { user-error "Could not find a C compiler. Tried: [join $try ", "]" } define CCACHE [find-an-executable [get-env CCACHE ccache]] # Initial cctest settings | | | | | | | 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 | if {[get-define CC] eq ""} { user-error "Could not find a C compiler. Tried: [join $try ", "]" } define CCACHE [find-an-executable [get-env CCACHE ccache]] # Initial cctest settings cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0} set autosetup(cc-include-deps) {} msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS]" if {[get-define CXX] ne "false"} { msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS]" } msg-result "Build C compiler...[get-define CC_FOR_BUILD]" # On Darwin, we prefer to use -g0 to avoid creating .dSYM directories # but some compilers may not support it, so test here. switch -glob -- [get-define host] { *-*-darwin* { if {[cctest -cflags {-g0}]} { define cc-default-debug -g0 } } } if {![cc-check-includes stdlib.h]} { user-error "Compiler does not work. See config.log" } |
Changes to autosetup/config.guess.
1 2 | #! /bin/sh # Attempt to guess a canonical system name. | < < | | | | < < | | > | | < < < < < < > > > < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2014 Free Software Foundation, Inc. timestamp='2014-03-23' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/>. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches with a ChangeLog entry to config-patches@gnu.org. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." |
︙ | ︙ | |||
88 89 90 91 92 93 94 | done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi | | | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; |
︙ | ︙ | |||
135 136 137 138 139 140 141 142 143 144 145 146 147 | PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or | > > > > > > > > > > > > > > > > > > > > > | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include <features.h> #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor |
︙ | ︙ | |||
177 178 179 180 181 182 183 | # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) | | > > > > | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; |
︙ | ︙ | |||
220 221 222 223 224 225 226 | exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) | | | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` |
︙ | ︙ | |||
266 267 268 269 270 271 272 | esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` | > | > > | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) |
︙ | ︙ | |||
292 293 294 295 296 297 298 | *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) | | | | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. |
︙ | ︙ | |||
391 392 393 394 395 396 397 | # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) | | | | | | | | | | | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 | # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) |
︙ | ︙ | |||
477 478 479 480 481 482 483 | m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) | | | | | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; |
︙ | ︙ | |||
591 592 593 594 595 596 597 | HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 | HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include <stdlib.h> #include <unistd.h> int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then |
︙ | ︙ | |||
727 728 729 730 731 732 733 | fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd | | | | | | | 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 | fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ |
︙ | ︙ | |||
766 767 768 769 770 771 772 | echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` | | | | | | | | > | < < | > > > | | | | | | | 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 | echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) |
︙ | ︙ | |||
848 849 850 851 852 853 854 | echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system | | | > > > > > > > | | | > > > | > > > | > > > | | | | | | < < < | | < < | | | | | > > > | | | | | | | | | > > > > > > | | | | | | | | | | | | | 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 | echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; |
︙ | ︙ | |||
1022 1023 1024 1025 1026 1027 1028 | if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) | | | 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 | if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; |
︙ | ︙ | |||
1050 1051 1052 1053 1054 1055 1056 | echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: | | | | | 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 | echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 |
︙ | ︙ | |||
1091 1092 1093 1094 1095 1096 1097 | test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) | | | | 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 | test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ |
︙ | ︙ | |||
1135 1136 1137 1138 1139 1140 1141 | if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; | | | | | | 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 | if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says <Richard.M.Bartel@ccMail.Census.GOV> echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes <hewes@openmarket.com>. # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. |
︙ | ︙ | |||
1164 1165 1166 1167 1168 1169 1170 | echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then | | | | > > > | 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 | echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) |
︙ | ︙ | |||
1207 1208 1209 1210 1211 1212 1213 | echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown | < < | > > > > | | | | | | > > > | | > > > > > > > | < > | | 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 | echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux |
︙ | ︙ | |||
1281 1282 1283 1284 1285 1286 1287 | *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) | | | < | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 | *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac cat >&2 <<EOF $0: unable to guess system type This script, last modified $timestamp, has failed to recognize the operating system you are using. It is advised that you download the most up to date version of the config scripts from |
︙ | ︙ |
Changes to autosetup/config.sub.
1 2 | #! /bin/sh # Configuration validation subroutine script. | < < | | < < < < | | | | | | | | < < | > > | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2014 Free Software Foundation, Inc. timestamp='2014-05-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/>. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches with a ChangeLog entry to config-patches@gnu.org. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: |
︙ | ︙ | |||
71 72 73 74 75 76 77 | -v, --version print version number, then exit Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) | < < | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | -v, --version print version number, then exit Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." |
︙ | ︙ | |||
121 122 123 124 125 126 127 | esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ | | > > > > | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac |
︙ | ︙ | |||
150 151 152 153 154 155 156 | ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ | | | | | | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; |
︙ | ︙ | |||
219 220 221 222 223 224 225 226 227 228 229 230 231 232 | ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) | > > > > > > | 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) |
︙ | ︙ | |||
243 244 245 246 247 248 249 250 251 252 | # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | > > | > > | > > > > | > > > | | | | | | | | | < > > > > > > > > > > > > > > > > | > | > > > | > > > > | > > | | | | | | > | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown |
︙ | ︙ | |||
420 421 422 423 424 425 426 | 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; | | | 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) |
︙ | ︙ | |||
503 504 505 506 507 508 509 | c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; | | | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 | c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; |
︙ | ︙ | |||
535 536 537 538 539 540 541 | basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; | | | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 | basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) |
︙ | ︙ | |||
693 694 695 696 697 698 699 | hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; | < | 731 732 733 734 735 736 737 738 739 740 741 742 743 744 | hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 |
︙ | ︙ | |||
751 752 753 754 755 756 757 | basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; | | > > > > | | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 | basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) |
︙ | ︙ | |||
790 791 792 793 794 795 796 797 798 799 800 | msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; mvs) basic_machine=i370-ibm os=-mvs ;; | > > > > < > | | | | 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 | msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd |
︙ | ︙ | |||
862 863 864 865 866 867 868 | nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; | | | | 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 | nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki |
︙ | ︙ | |||
950 951 952 953 954 955 956 | basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; | | > | | 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 | basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; |
︙ | ︙ | |||
977 978 979 980 981 982 983 | ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; | > > | > > | 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; |
︙ | ︙ | |||
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 | st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; | > > > | 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 | st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; |
︙ | ︙ | |||
1102 1103 1104 1105 1106 1107 1108 | basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; | < < < < < | | 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 | basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown |
︙ | ︙ | |||
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 | xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim | > > > | 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 | xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim |
︙ | ︙ | |||
1275 1276 1277 1278 1279 1280 1281 | esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in | | | | | | 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 | esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; |
︙ | ︙ | |||
1303 1304 1305 1306 1307 1308 1309 | # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | | | | | | | 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 | # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) |
︙ | ︙ | |||
1364 1365 1366 1367 1368 1369 1370 | ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; | | | 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 | ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose |
︙ | ︙ | |||
1413 1414 1415 1416 1417 1418 1419 | # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; | | | 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 | # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 |
︙ | ︙ | |||
1449 1450 1451 1452 1453 1454 1455 | ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; | < < < | | | 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 | ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 |
︙ | ︙ | |||
1482 1483 1484 1485 1486 1487 1488 | # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in | | | | | > > > > > > | 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 | # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; |
︙ | ︙ | |||
1527 1528 1529 1530 1531 1532 1533 | os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 | < < < | | 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 | os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf |
︙ | ︙ | |||
1561 1562 1563 1564 1565 1566 1567 | ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; | | | 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 | ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf |
︙ | ︙ |
Changes to autosetup/jimsh0.c.
|
| | | 1 2 3 4 5 6 7 8 | /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */ #define _GNU_SOURCE #define JIM_TCL_COMPAT #define JIM_REFERENCES #define JIM_ANSIC #define JIM_REGEXP #define HAVE_NO_AUTOCONF #define _JIMAUTOCONF_H |
︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | #else #define TCL_PLATFORM_OS "unknown" #define TCL_PLATFORM_PLATFORM "unix" #define TCL_PLATFORM_PATH_SEPARATOR ":" #define HAVE_VFORK #define HAVE_WAITPID #define HAVE_ISATTY #define HAVE_SYS_TIME_H #define HAVE_DIRENT_H #define HAVE_UNISTD_H #endif #ifndef JIM_WIN32COMPAT_H #define JIM_WIN32COMPAT_H #if defined(_WIN32) || defined(WIN32) #define HAVE_DLOPEN void *dlopen(const char *path, int mode); int dlclose(void *handle); | > > > > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #else #define TCL_PLATFORM_OS "unknown" #define TCL_PLATFORM_PLATFORM "unix" #define TCL_PLATFORM_PATH_SEPARATOR ":" #define HAVE_VFORK #define HAVE_WAITPID #define HAVE_ISATTY #define HAVE_MKSTEMP #define HAVE_LINK #define HAVE_SYS_TIME_H #define HAVE_DIRENT_H #define HAVE_UNISTD_H #endif #define JIM_VERSION 76 #ifndef JIM_WIN32COMPAT_H #define JIM_WIN32COMPAT_H #ifdef __cplusplus extern "C" { #endif #if defined(_WIN32) || defined(WIN32) #define HAVE_DLOPEN void *dlopen(const char *path, int mode); int dlclose(void *handle); |
︙ | ︙ | |||
103 104 105 106 107 108 109 | struct dirent result; char *name; } DIR; DIR *opendir(const char *name); int closedir(DIR *dir); struct dirent *readdir(DIR *dir); | > > > > > > | > > > > > > > > > > > > > > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | struct dirent result; char *name; } DIR; DIR *opendir(const char *name); int closedir(DIR *dir); struct dirent *readdir(DIR *dir); #elif defined(__MINGW32__) #include <stdlib.h> #define strtod __strtod #endif #endif #ifdef __cplusplus } #endif #endif #ifndef UTF8_UTIL_H #define UTF8_UTIL_H #ifdef __cplusplus extern "C" { #endif #define MAX_UTF8_LEN 4 int utf8_fromunicode(char *p, unsigned uc); #ifndef JIM_UTF8 #include <ctype.h> #define utf8_strlen(S, B) ((B) < 0 ? strlen(S) : (B)) #define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1) #define utf8_getchars(CP, C) (*(CP) = (C), 1) #define utf8_upper(C) toupper(C) #define utf8_title(C) toupper(C) #define utf8_lower(C) tolower(C) #define utf8_index(C, I) (I) #define utf8_charlen(C) 1 #define utf8_prev_len(S, L) 1 #else #endif #ifdef __cplusplus } #endif #endif #ifndef __JIM__H #define __JIM__H #ifdef __cplusplus |
︙ | ︙ | |||
183 184 185 186 187 188 189 | # define strtoull strtoul # endif #endif #define UCHAR(c) ((unsigned char)(c)) | < < | | | | | | | < < < < < < < < < | < | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | # define strtoull strtoul # endif #endif #define UCHAR(c) ((unsigned char)(c)) #define JIM_OK 0 #define JIM_ERR 1 #define JIM_RETURN 2 #define JIM_BREAK 3 #define JIM_CONTINUE 4 #define JIM_SIGNAL 5 #define JIM_EXIT 6 #define JIM_EVAL 7 #define JIM_MAX_CALLFRAME_DEPTH 1000 #define JIM_MAX_EVAL_DEPTH 2000 #define JIM_PRIV_FLAG_SHIFT 20 #define JIM_NONE 0 #define JIM_ERRMSG 1 #define JIM_ENUM_ABBREV 2 #define JIM_UNSHARED 4 #define JIM_MUSTEXIST 8 #define JIM_SUBST_NOVAR 1 #define JIM_SUBST_NOCMD 2 #define JIM_SUBST_NOESC 4 #define JIM_SUBST_FLAG 128 #define JIM_CASESENS 0 #define JIM_NOCASE 1 #define JIM_PATH_LEN 1024 #define JIM_NOTUSED(V) ((void) V) #define JIM_LIBPATH "auto_path" #define JIM_INTERACTIVE "tcl_interactive" typedef struct Jim_Stack { int len; |
︙ | ︙ | |||
265 266 267 268 269 270 271 272 273 274 275 | void (*keyDestructor)(void *privdata, void *key); void (*valDestructor)(void *privdata, void *obj); } Jim_HashTableType; typedef struct Jim_HashTable { Jim_HashEntry **table; const Jim_HashTableType *type; unsigned int size; unsigned int sizemask; unsigned int used; unsigned int collisions; | > | < > | | | | | | | | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | void (*keyDestructor)(void *privdata, void *key); void (*valDestructor)(void *privdata, void *obj); } Jim_HashTableType; typedef struct Jim_HashTable { Jim_HashEntry **table; const Jim_HashTableType *type; void *privdata; unsigned int size; unsigned int sizemask; unsigned int used; unsigned int collisions; unsigned int uniq; } Jim_HashTable; typedef struct Jim_HashTableIterator { Jim_HashTable *ht; Jim_HashEntry *entry, *nextEntry; int index; } Jim_HashTableIterator; #define JIM_HT_INITIAL_SIZE 16 #define Jim_FreeEntryVal(ht, entry) \ if ((ht)->type->valDestructor) \ (ht)->type->valDestructor((ht)->privdata, (entry)->u.val) #define Jim_SetHashVal(ht, entry, _val_) do { \ if ((ht)->type->valDup) \ (entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \ else \ (entry)->u.val = (_val_); \ } while(0) #define Jim_FreeEntryKey(ht, entry) \ if ((ht)->type->keyDestructor) \ (ht)->type->keyDestructor((ht)->privdata, (entry)->key) #define Jim_SetHashKey(ht, entry, _key_) do { \ if ((ht)->type->keyDup) \ (entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \ else \ (entry)->key = (void *)(_key_); \ } while(0) #define Jim_CompareHashKeys(ht, key1, key2) \ (((ht)->type->keyCompare) ? \ (ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \ (key1) == (key2)) #define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq) #define Jim_GetHashEntryKey(he) ((he)->key) #define Jim_GetHashEntryVal(he) ((he)->u.val) #define Jim_GetHashTableCollisions(ht) ((ht)->collisions) #define Jim_GetHashTableSize(ht) ((ht)->size) #define Jim_GetHashTableUsed(ht) ((ht)->used) typedef struct Jim_Obj { char *bytes; |
︙ | ︙ | |||
340 341 342 343 344 345 346 | struct { void *ptr1; void *ptr2; } twoPtrValue; struct { | < > < > | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | struct { void *ptr1; void *ptr2; } twoPtrValue; struct { struct Jim_Var *varPtr; unsigned long callFrameId; int global; } varValue; struct { struct Jim_Obj *nsObj; struct Jim_Cmd *cmdPtr; unsigned long procEpoch; } cmdValue; struct { struct Jim_Obj **ele; int len; int maxLen; } listValue; |
︙ | ︙ | |||
378 379 380 381 382 383 384 | struct { struct Jim_Obj *varNameObjPtr; struct Jim_Obj *indexObjPtr; } dictSubstValue; struct { | < > | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | struct { struct Jim_Obj *varNameObjPtr; struct Jim_Obj *indexObjPtr; } dictSubstValue; struct { void *compre; unsigned flags; } regexpValue; struct { int line; int argc; } scriptLineValue; } internalRep; struct Jim_Obj *prevObjPtr; |
︙ | ︙ | |||
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | Jim_Obj *procArgsObjPtr; Jim_Obj *procBodyObjPtr; struct Jim_CallFrame *next; Jim_Obj *nsObj; Jim_Obj *fileNameObj; int line; Jim_Stack *localCommands; } Jim_CallFrame; typedef struct Jim_Var { Jim_Obj *objPtr; struct Jim_CallFrame *linkFramePtr; } Jim_Var; | > > > | | | | | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | Jim_Obj *procArgsObjPtr; Jim_Obj *procBodyObjPtr; struct Jim_CallFrame *next; Jim_Obj *nsObj; Jim_Obj *fileNameObj; int line; Jim_Stack *localCommands; int tailcall; struct Jim_Obj *tailcallObj; struct Jim_Cmd *tailcallCmd; } Jim_CallFrame; typedef struct Jim_Var { Jim_Obj *objPtr; struct Jim_CallFrame *linkFramePtr; } Jim_Var; typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc, Jim_Obj *const *argv); typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData); typedef struct Jim_Cmd { int inUse; int isproc; struct Jim_Cmd *prevCmd; union { struct { Jim_CmdProc *cmdProc; Jim_DelCmdProc *delProc; void *privData; } native; struct { Jim_Obj *argListObjPtr; Jim_Obj *bodyObjPtr; Jim_HashTable *staticVars; |
︙ | ︙ | |||
588 589 590 591 592 593 594 | typedef struct Jim_Reference { Jim_Obj *objPtr; Jim_Obj *finalizerCmdNamePtr; char tag[JIM_REFERENCE_TAGLEN+1]; } Jim_Reference; | < < < < > | 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 | typedef struct Jim_Reference { Jim_Obj *objPtr; Jim_Obj *finalizerCmdNamePtr; char tag[JIM_REFERENCE_TAGLEN+1]; } Jim_Reference; #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0) #define Jim_FreeHashTableIterator(iter) Jim_Free(iter) #define JIM_EXPORT JIM_EXPORT void *Jim_Alloc (int size); JIM_EXPORT void *Jim_Realloc(void *ptr, int size); JIM_EXPORT void Jim_Free (void *ptr); JIM_EXPORT char * Jim_StrDup (const char *s); JIM_EXPORT char *Jim_StrDupLen(const char *s, int l); JIM_EXPORT char **Jim_GetEnviron(void); JIM_EXPORT void Jim_SetEnviron(char **env); JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *template); JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script); JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script); |
︙ | ︙ | |||
738 739 740 741 742 743 744 | JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp, const char *name, Jim_Obj *objPtr); JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp, const char *name, const char *val); JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame); | | | < | 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 | JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp, const char *name, Jim_Obj *objPtr); JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp, const char *name, const char *val); JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame); JIM_EXPORT Jim_Obj * Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr); JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags); JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags); JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp, const char *name, int flags); JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp, |
︙ | ︙ | |||
803 804 805 806 807 808 809 810 811 812 813 814 815 816 | JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len); JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr); JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj); JIM_EXPORT int Jim_DictValues(Jim_Interp *interp, Jim_Obj *dictObjPtr, Jim_Obj *patternObjPtr); JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr); JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr); JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp, | > | 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 | JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len); JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr); JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj); JIM_EXPORT int Jim_DictValues(Jim_Interp *interp, Jim_Obj *dictObjPtr, Jim_Obj *patternObjPtr); JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr); JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr); JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr); JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp, |
︙ | ︙ | |||
830 831 832 833 834 835 836 | JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr); JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue); JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue); | < < < < < < | 846 847 848 849 850 851 852 853 854 855 856 857 858 859 | JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr); JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue); JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue); JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg); JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr, const char * const *tablePtr, int *indexPtr, const char *name, int flags); JIM_EXPORT int Jim_ScriptIsComplete (const char *s, int len, char *stateCharPtr); |
︙ | ︙ | |||
873 874 875 876 877 878 879 | JIM_EXPORT char *Jim_HistoryGetline(const char *prompt); JIM_EXPORT void Jim_HistoryAdd(const char *line); JIM_EXPORT void Jim_HistoryShow(void); JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp); JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); | | > < | 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 | JIM_EXPORT char *Jim_HistoryGetline(const char *prompt); JIM_EXPORT void Jim_HistoryAdd(const char *line); JIM_EXPORT void Jim_HistoryShow(void); JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp); JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); JIM_EXPORT int Jim_IsBigEndian(void); #define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask) JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp); JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command); JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr); JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr); #ifdef __cplusplus } |
︙ | ︙ | |||
908 909 910 911 912 913 914 | #define JIM_MODFLAG_HIDDEN 0x0001 #define JIM_MODFLAG_FULLARGV 0x0002 | | | < < < < | < | | < | 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 | #define JIM_MODFLAG_HIDDEN 0x0001 #define JIM_MODFLAG_FULLARGV 0x0002 typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv); typedef struct { const char *cmd; const char *args; jim_subcmd_function *function; short minargs; short maxargs; unsigned short flags; } jim_subcmd_type; const jim_subcmd_type * Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv); int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv); #ifdef __cplusplus } #endif #endif #ifndef JIMREGEXP_H #define JIMREGEXP_H #ifdef __cplusplus extern "C" { #endif #include <stdlib.h> typedef struct { int rm_so; int rm_eo; } regmatch_t; |
︙ | ︙ | |||
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 | }; int regcomp(regex_t *preg, const char *regex, int cflags); int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags); size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size); void regfree(regex_t *preg); #endif #endif int Jim_bootstrapInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG)) return JIM_ERR; | > > | 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 | }; int regcomp(regex_t *preg, const char *regex, int cflags); int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags); size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size); void regfree(regex_t *preg); #ifdef __cplusplus } #endif #endif int Jim_bootstrapInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG)) return JIM_ERR; |
︙ | ︙ | |||
1043 1044 1045 1046 1047 1048 1049 1050 1051 | return Jim_EvalSource(interp, "initjimsh.tcl", 1, "\n" "\n" "\n" "proc _jimsh_init {} {\n" " rename _jimsh_init {}\n" "\n" "\n" | > > > > > > > > > > > > > > > > | > > > | < | | > | | 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 | return Jim_EvalSource(interp, "initjimsh.tcl", 1, "\n" "\n" "\n" "proc _jimsh_init {} {\n" " rename _jimsh_init {}\n" " global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n" "\n" "\n" " if {[exists jim::argv0]} {\n" " if {[string match \"*/*\" $jim::argv0]} {\n" " set jim::exe [file join [pwd] $jim::argv0]\n" " } else {\n" " foreach path [split [env PATH \"\"] $tcl_platform(pathSeparator)] {\n" " set exec [file join [pwd] [string map {\\\\ /} $path] $jim::argv0]\n" " if {[file executable $exec]} {\n" " set jim::exe $exec\n" " break\n" " }\n" " }\n" " }\n" " }\n" "\n" "\n" " lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n" " if {[exists jim::exe]} {\n" " lappend p [file dirname $jim::exe]\n" " }\n" " lappend p {*}$auto_path\n" " set auto_path $p\n" "\n" " if {$tcl_interactive && [env HOME {}] ne \"\"} {\n" " foreach src {.jimrc jimrc.tcl} {\n" " if {[file exists [env HOME]/$src]} {\n" " uplevel #0 source [env HOME]/$src\n" " break\n" " }\n" " }\n" " }\n" " return \"\"\n" "}\n" "\n" "if {$tcl_platform(platform) eq \"windows\"} {\n" " set jim::argv0 [string map {\\\\ /} $jim::argv0]\n" "}\n" "\n" "_jimsh_init\n" ); } int Jim_globInit(Jim_Interp *interp) { |
︙ | ︙ | |||
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 | "\n" "\n" "\n" "package require readdir\n" "\n" "\n" "proc glob.globdir {dir pattern} {\n" " set result {}\n" " set files [readdir $dir]\n" " lappend files . ..\n" "\n" " foreach name $files {\n" " if {[string match $pattern $name]} {\n" "\n" | > > > > > | 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 | "\n" "\n" "\n" "package require readdir\n" "\n" "\n" "proc glob.globdir {dir pattern} {\n" " if {[file exists $dir/$pattern]} {\n" "\n" " return [list $pattern]\n" " }\n" "\n" " set result {}\n" " set files [readdir $dir]\n" " lappend files . ..\n" "\n" " foreach name $files {\n" " if {[string match $pattern $name]} {\n" "\n" |
︙ | ︙ | |||
1143 1144 1145 1146 1147 1148 1149 | " break\n" " }\n" " }\n" "\n" " foreach old $oldexp {\n" " lappend newexp $old$suf\n" " }\n" | | | 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 | " break\n" " }\n" " }\n" "\n" " foreach old $oldexp {\n" " lappend newexp $old$suf\n" " }\n" " list $rest {*}$newexp\n" "}\n" "\n" "\n" "\n" "proc glob.glob {base pattern} {\n" " set dir [file dirname $pattern]\n" " if {$pattern eq $dir || $pattern eq \"\"} {\n" |
︙ | ︙ | |||
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 | "\n" "\n" "\n" "\n" "proc glob {args} {\n" " set nocomplain 0\n" " set base \"\"\n" "\n" " set n 0\n" " foreach arg $args {\n" " if {[info exists param]} {\n" " set $param $arg\n" " unset param\n" " incr n\n" " continue\n" " }\n" " switch -glob -- $arg {\n" " -d* {\n" " set switch $arg\n" " set param base\n" " }\n" " -n* {\n" " set nocomplain 1\n" " }\n" | > | < | < < < > > > | | > > | > > > > | > > | 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 | "\n" "\n" "\n" "\n" "proc glob {args} {\n" " set nocomplain 0\n" " set base \"\"\n" " set tails 0\n" "\n" " set n 0\n" " foreach arg $args {\n" " if {[info exists param]} {\n" " set $param $arg\n" " unset param\n" " incr n\n" " continue\n" " }\n" " switch -glob -- $arg {\n" " -d* {\n" " set switch $arg\n" " set param base\n" " }\n" " -n* {\n" " set nocomplain 1\n" " }\n" " -ta* {\n" " set tails 1\n" " }\n" " -- {\n" " incr n\n" " break\n" " }\n" " -* {\n" " return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n" " }\n" " * {\n" " break\n" " }\n" " }\n" " incr n\n" " }\n" " if {[info exists param]} {\n" " return -code error \"missing argument to \\\"$switch\\\"\"\n" " }\n" " if {[llength $args] <= $n} {\n" " return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n" " }\n" "\n" " set args [lrange $args $n end]\n" "\n" " set result {}\n" " foreach pattern $args {\n" " set escpattern [string map {\n" " \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n" " } $pattern]\n" " set patexps [lassign [glob.explode $escpattern] rest]\n" " if {$rest ne \"\"} {\n" " return -code error \"unmatched close brace in glob pattern\"\n" " }\n" " foreach patexp $patexps {\n" " set patexp [string map {\n" " \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n" " } $patexp]\n" " foreach {realname name} [glob.glob $base $patexp] {\n" " incr n\n" " if {$tails} {\n" " lappend result $name\n" " } else {\n" " lappend result [file join $base $name]\n" " }\n" " }\n" " }\n" " }\n" "\n" " if {!$nocomplain && [llength $result] == 0} {\n" " set s $(([llength $args] > 1) ? \"s\" : \"\")\n" " return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n" " }\n" "\n" " return $result\n" "}\n" ); } int Jim_stdlibInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG)) return JIM_ERR; return Jim_EvalSource(interp, "stdlib.tcl", 1, "\n" "\n" "\n" "proc lambda {arglist args} {\n" " tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n" "}\n" "\n" "proc lambda.finalizer {name val} {\n" " rename $name {}\n" |
︙ | ︙ | |||
1295 1296 1297 1298 1299 1300 1301 | "proc function {value} {\n" " return $value\n" "}\n" "\n" "\n" "\n" "\n" | | > | < | < | < < | < | | | > > > | > > > | | < < < < < < | < < < | < | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 | "proc function {value} {\n" " return $value\n" "}\n" "\n" "\n" "\n" "\n" "proc stacktrace {{skip 0}} {\n" " set trace {}\n" " incr skip\n" " foreach level [range $skip [info level]] {\n" " lappend trace {*}[info frame -$level]\n" " }\n" " return $trace\n" "}\n" "\n" "\n" "proc stackdump {stacktrace} {\n" " set lines {}\n" " foreach {l f p} [lreverse $stacktrace] {\n" " set line {}\n" " if {$p ne \"\"} {\n" " append line \"in procedure '$p' \"\n" " if {$f ne \"\"} {\n" " append line \"called \"\n" " }\n" " }\n" " if {$f ne \"\"} {\n" " append line \"at file \\\"$f\\\", line $l\"\n" " }\n" " if {$line ne \"\"} {\n" " lappend lines $line\n" " }\n" " }\n" " join $lines \\n\n" "}\n" "\n" "\n" "\n" "proc errorInfo {msg {stacktrace \"\"}} {\n" " if {$stacktrace eq \"\"} {\n" "\n" " set stacktrace [info stacktrace]\n" "\n" " lappend stacktrace {*}[stacktrace 1]\n" " }\n" " lassign $stacktrace p f l\n" " if {$f ne \"\"} {\n" " set result \"$f:$l: Error: \"\n" " }\n" " append result \"$msg\\n\"\n" " append result [stackdump $stacktrace]\n" "\n" "\n" " string trim $result\n" "}\n" "\n" "\n" "\n" "proc {info nameofexecutable} {} {\n" " if {[exists ::jim::exe]} {\n" " return $::jim::exe\n" " }\n" "}\n" "\n" "\n" "proc {dict with} {&dictVar {args key} script} {\n" " set keys {}\n" " foreach {n v} [dict get $dictVar {*}$key] {\n" " upvar $n var_$n\n" " set var_$n $v\n" " lappend keys $n\n" " }\n" " catch {uplevel 1 $script} msg opts\n" " if {[info exists dictVar] && ([llength $key] == 0 || [dict exists $dictVar {*}$key])} {\n" " foreach n $keys {\n" " if {[info exists var_$n]} {\n" " dict set dictVar {*}$key $n [set var_$n]\n" " } else {\n" " dict unset dictVar {*}$key $n\n" " }\n" " }\n" " }\n" " return {*}$opts $msg\n" "}\n" "\n" "\n" "proc {dict update} {&varName args script} {\n" " set keys {}\n" " foreach {n v} $args {\n" " upvar $v var_$v\n" " if {[dict exists $varName $n]} {\n" " set var_$v [dict get $varName $n]\n" " }\n" " }\n" " catch {uplevel 1 $script} msg opts\n" " if {[info exists varName]} {\n" " foreach {n v} $args {\n" " if {[info exists var_$v]} {\n" " dict set varName $n [set var_$v]\n" " } else {\n" " dict unset varName $n\n" " }\n" " }\n" " }\n" " return {*}$opts $msg\n" "}\n" "\n" "\n" "\n" "proc {dict merge} {dict args} {\n" " foreach d $args {\n" "\n" " dict size $d\n" " foreach {k v} $d {\n" " dict set dict $k $v\n" " }\n" " }\n" " return $dict\n" "}\n" "\n" "proc {dict replace} {dictionary {args {key value}}} {\n" " if {[llength ${key value}] % 2} {\n" " tailcall {dict replace}\n" " }\n" " tailcall dict merge $dictionary ${key value}\n" "}\n" "\n" "\n" "proc {dict lappend} {varName key {args value}} {\n" " upvar $varName dict\n" " if {[exists dict] && [dict exists $dict $key]} {\n" " set list [dict get $dict $key]\n" " }\n" " lappend list {*}$value\n" " dict set dict $key $list\n" "}\n" "\n" "\n" "proc {dict append} {varName key {args value}} {\n" " upvar $varName dict\n" " if {[exists dict] && [dict exists $dict $key]} {\n" " set str [dict get $dict $key]\n" " }\n" " append str {*}$value\n" " dict set dict $key $str\n" "}\n" "\n" "\n" "proc {dict incr} {varName key {increment 1}} {\n" " upvar $varName dict\n" " if {[exists dict] && [dict exists $dict $key]} {\n" " set value [dict get $dict $key]\n" " }\n" " incr value $increment\n" " dict set dict $key $value\n" "}\n" "\n" "\n" "proc {dict remove} {dictionary {args key}} {\n" " foreach k $key {\n" " dict unset dictionary $k\n" " }\n" " return $dictionary\n" "}\n" "\n" "\n" "proc {dict values} {dictionary {pattern *}} {\n" " dict keys [lreverse $dictionary] $pattern\n" "}\n" "\n" "\n" "proc {dict for} {vars dictionary script} {\n" " if {[llength $vars] != 2} {\n" " return -code error \"must have exactly two variable names\"\n" " }\n" " dict size $dictionary\n" " tailcall foreach $vars $dictionary $script\n" "}\n" ); } int Jim_tclcompatInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG)) return JIM_ERR; return Jim_EvalSource(interp, "tclcompat.tcl", 1, "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "set env [env]\n" "\n" "\n" "if {[info commands stdout] ne \"\"} {\n" "\n" " foreach p {gets flush close eof seek tell} {\n" " proc $p {chan args} {p} {\n" " tailcall $chan $p {*}$args\n" " }\n" |
︙ | ︙ | |||
1460 1461 1462 1463 1464 1465 1466 | " return -code error \"fconfigure: unknown option $n\"\n" " }\n" " }\n" " }\n" " }\n" "}\n" "\n" | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 | " return -code error \"fconfigure: unknown option $n\"\n" " }\n" " }\n" " }\n" " }\n" "}\n" "\n" "\n" "proc fileevent {args} {\n" " tailcall {*}$args\n" "}\n" "\n" "\n" "\n" |
︙ | ︙ | |||
1532 1533 1534 1535 1536 1537 1538 | "\n" "proc {file copy} {{force {}} source target} {\n" " try {\n" " if {$force ni {{} -force}} {\n" " error \"bad option \\\"$force\\\": should be -force\"\n" " }\n" "\n" | | | | | > > > > > > > > > > > > | | 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 | "\n" "proc {file copy} {{force {}} source target} {\n" " try {\n" " if {$force ni {{} -force}} {\n" " error \"bad option \\\"$force\\\": should be -force\"\n" " }\n" "\n" " set in [open $source rb]\n" "\n" " if {[file exists $target]} {\n" " if {$force eq \"\"} {\n" " error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n" " }\n" "\n" " if {$source eq $target} {\n" " return\n" " }\n" "\n" "\n" " file stat $source ss\n" " file stat $target ts\n" " if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n" " return\n" " }\n" " }\n" " set out [open $target wb]\n" " $in copyto $out\n" " $out close\n" " } on error {msg opts} {\n" " incr opts(-level)\n" " return {*}$opts $msg\n" " } finally {\n" " catch {$in close}\n" |
︙ | ︙ | |||
1585 1586 1587 1588 1589 1590 1591 | " $r close\n" " $w close\n" " error $error\n" " }\n" "}\n" "\n" "\n" | | | | | | | 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 | " $r close\n" " $w close\n" " error $error\n" " }\n" "}\n" "\n" "\n" "local proc pid {{channelId {}}} {\n" " if {$channelId eq \"\"} {\n" " tailcall upcall pid\n" " }\n" " if {[catch {$channelId tell}]} {\n" " return -code error \"can not find channel named \\\"$channelId\\\"\"\n" " }\n" " if {[catch {$channelId pid} pids]} {\n" " return \"\"\n" " }\n" " return $pids\n" "}\n" "\n" "\n" "\n" |
︙ | ︙ | |||
1624 1625 1626 1627 1628 1629 1630 | " }\n" " lappend catchopts $opt\n" " }\n" " if {[llength $args] == 0} {\n" " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n" " }\n" " set args [lassign $args script]\n" | | | | | 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 | " }\n" " lappend catchopts $opt\n" " }\n" " if {[llength $args] == 0} {\n" " return -code error {wrong # args: should be \"try ?options? script ?argument ...?\"}\n" " }\n" " set args [lassign $args script]\n" " set code [catch -eval {*}$catchopts {uplevel 1 $script} msg opts]\n" "\n" " set handled 0\n" "\n" " foreach {on codes vars script} $args {\n" " switch -- $on \\\n" " on {\n" " if {!$handled && ($codes eq \"*\" || [info returncode $code] in $codes)} {\n" " lassign $vars msgvar optsvar\n" " if {$msgvar ne \"\"} {\n" " upvar $msgvar hmsg\n" " set hmsg $msg\n" " }\n" " if {$optsvar ne \"\"} {\n" " upvar $optsvar hopts\n" " set hopts $opts\n" " }\n" "\n" " set code [catch {uplevel 1 $script} msg opts]\n" " incr handled\n" " }\n" " } \\\n" " finally {\n" " set finalcode [catch {uplevel 1 $codes} finalmsg finalopts]\n" " if {$finalcode} {\n" "\n" " set code $finalcode\n" " set msg $finalmsg\n" " set opts $finalopts\n" " }\n" " break\n" |
︙ | ︙ | |||
1685 1686 1687 1688 1689 1690 1691 | " }\n" " file delete $path\n" "}\n" ); } | < > > > > < | 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 | " }\n" " file delete $path\n" "}\n" ); } #include <stdio.h> #include <string.h> #include <errno.h> #include <fcntl.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #include <sys/stat.h> #endif #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H) #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #ifdef HAVE_SYS_UN_H #include <sys/un.h> #endif #else #define JIM_ANSIC #endif |
︙ | ︙ | |||
1733 1734 1735 1736 1737 1738 1739 | typedef struct AioFile { FILE *fp; Jim_Obj *filename; int type; | | < < < | 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 | typedef struct AioFile { FILE *fp; Jim_Obj *filename; int type; int openFlags; int fd; Jim_Obj *rEvent; Jim_Obj *wEvent; Jim_Obj *eEvent; int addr_family; } AioFile; static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); |
︙ | ︙ | |||
1765 1766 1767 1768 1769 1770 1771 | static void JimAioDelProc(Jim_Interp *interp, void *privData) { AioFile *af = privData; JIM_NOTUSED(interp); | < < < < < | > | | | < < | < | 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 | static void JimAioDelProc(Jim_Interp *interp, void *privData) { AioFile *af = privData; JIM_NOTUSED(interp); Jim_DecrRefCount(interp, af->filename); #ifdef jim_ext_eventloop Jim_DeleteFileHandler(interp, af->fp, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION); #endif if (!(af->openFlags & AIO_KEEPOPEN)) { fclose(af->fp); } Jim_Free(af); } static int JimCheckStreamError(Jim_Interp *interp, AioFile *af) { if (!ferror(af->fp)) { return JIM_OK; |
︙ | ︙ | |||
2033 2034 2035 2036 2037 2038 2039 | Jim_SetResultInt(interp, feof(af->fp)); return JIM_OK; } static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { | > > > > > > | > > > > | > > > > > > > > > | 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 | Jim_SetResultInt(interp, feof(af->fp)); return JIM_OK; } static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc == 3) { #if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN) static const char * const options[] = { "r", "w", NULL }; enum { OPT_R, OPT_W, }; int option; AioFile *af = Jim_CmdPrivData(interp); if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { return JIM_ERR; } if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) { return JIM_OK; } JimAioSetError(interp, NULL); #else Jim_SetResultString(interp, "async close not supported", -1); #endif return JIM_ERR; } return Jim_DeleteCommand(interp, Jim_String(argv[0])); } static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); int orig = SEEK_SET; jim_wide offset; |
︙ | ︙ | |||
2085 2086 2087 2088 2089 2090 2091 | } #ifdef O_NDELAY static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); | | | < | 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 | } #ifdef O_NDELAY static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); int fmode = fcntl(af->fd, F_GETFL); if (argc) { long nb; if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) { return JIM_ERR; } if (nb) { fmode |= O_NDELAY; } else { fmode &= ~O_NDELAY; } (void)fcntl(af->fd, F_SETFL, fmode); } Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0); return JIM_OK; } #endif static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv) |
︙ | ︙ | |||
2145 2146 2147 2148 2149 2150 2151 | } return JIM_OK; } #ifdef jim_ext_eventloop static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData) { | | | > | | < < | < < | | | 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 | } return JIM_OK; } #ifdef jim_ext_eventloop static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData) { Jim_Obj **objPtrPtr = clientData; Jim_DecrRefCount(interp, *objPtrPtr); *objPtrPtr = NULL; } static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask) { Jim_Obj **objPtrPtr = clientData; return Jim_EvalObjBackground(interp, *objPtrPtr); } static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj, int argc, Jim_Obj * const *argv) { if (argc == 0) { if (*scriptHandlerObj) { Jim_SetResult(interp, *scriptHandlerObj); } return JIM_OK; } if (*scriptHandlerObj) { Jim_DeleteFileHandler(interp, af->fp, mask); } if (Jim_Length(argv[0]) == 0) { return JIM_OK; } Jim_IncrRefCount(argv[0]); *scriptHandlerObj = argv[0]; Jim_CreateFileHandler(interp, af->fp, mask, JimAioFileEventHandler, scriptHandlerObj, JimAioFileEventFinalizer); return JIM_OK; } static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); |
︙ | ︙ | |||
2211 2212 2213 2214 2215 2216 2217 | return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv); } static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); | | | 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 | return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv); } static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->eEvent, argc, argv); } #endif static const jim_subcmd_type aio_command_table[] = { { "read", "?-nonewline? ?len?", aio_cmd_read, |
︙ | ︙ | |||
2266 2267 2268 2269 2270 2271 2272 | NULL, aio_cmd_eof, 0, 0, }, { "close", | < > < > | 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 | NULL, aio_cmd_eof, 0, 0, }, { "close", "?r(ead)|w(rite)?", aio_cmd_close, 0, 1, JIM_MODFLAG_FULLARGV, }, { "seek", "offset ?start|current|end", aio_cmd_seek, 1, |
︙ | ︙ | |||
2345 2346 2347 2348 2349 2350 2351 | return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv); } static int JimAioOpenCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *mode; | < < | > > > | | | | | | > | | > > | < < < < | | > | < | | | | | | | > | | | < < < > | > | > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 | return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv); } static int JimAioOpenCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *mode; if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?"); return JIM_ERR; } mode = (argc == 3) ? Jim_String(argv[2]) : "r"; #ifdef jim_ext_tclcompat { const char *filename = Jim_String(argv[1]); if (*filename == '|') { Jim_Obj *evalObj[3]; evalObj[0] = Jim_NewStringObj(interp, "::popen", -1); evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1); evalObj[2] = Jim_NewStringObj(interp, mode, -1); return Jim_EvalObjVector(interp, 3, evalObj); } } #endif return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode); } static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename, const char *hdlfmt, int family, const char *mode) { AioFile *af; char buf[AIO_CMD_LEN]; int openFlags = 0; if (fh) { filename = Jim_NewStringObj(interp, hdlfmt, -1); openFlags = AIO_KEEPOPEN; } Jim_IncrRefCount(filename); if (fh == NULL) { #if !defined(JIM_ANSIC) if (fd >= 0) { fh = fdopen(fd, mode); } else #endif fh = fopen(Jim_String(filename), mode); if (fh == NULL) { JimAioSetError(interp, filename); #if !defined(JIM_ANSIC) if (fd >= 0) { close(fd); } #endif Jim_DecrRefCount(interp, filename); return JIM_ERR; } } af = Jim_Alloc(sizeof(*af)); memset(af, 0, sizeof(*af)); af->fp = fh; af->fd = fileno(fh); af->filename = filename; #ifdef FD_CLOEXEC if ((openFlags & AIO_KEEPOPEN) == 0) { (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC); } #endif af->openFlags = openFlags; af->addr_family = family; snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp)); Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc); Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1))); return JIM_OK; } #if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && defined(HAVE_SYS_UN_H)) static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename, const char *hdlfmt, int family, const char *mode[2]) { if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0]) == JIM_OK) { Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1]) == JIM_OK) { Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); Jim_SetResult(interp, objPtr); return JIM_OK; } } close(p[0]); close(p[1]); JimAioSetError(interp, NULL); return JIM_ERR; } #endif int Jim_MakeTempFile(Jim_Interp *interp, const char *template) { #ifdef HAVE_MKSTEMP int fd; mode_t mask; Jim_Obj *filenameObj; if (template == NULL) { const char *tmpdir = getenv("TMPDIR"); if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) { tmpdir = "/tmp/"; } filenameObj = Jim_NewStringObj(interp, tmpdir, -1); if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') { Jim_AppendString(interp, filenameObj, "/", 1); } Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1); } else { filenameObj = Jim_NewStringObj(interp, template, -1); } mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); fd = mkstemp(filenameObj->bytes); umask(mask); if (fd < 0) { JimAioSetError(interp, filenameObj); Jim_FreeNewObj(interp, filenameObj); return -1; } Jim_SetResult(interp, filenameObj); return fd; #else Jim_SetResultString(interp, "platform has no tempfile support", -1); return -1; #endif } FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command) { Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG); if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) { return ((AioFile *) cmdPtr->u.native.privData)->fp; } Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command); return NULL; } |
︙ | ︙ | |||
2459 2460 2461 2462 2463 2464 2465 | JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r"); JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w"); JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w"); return JIM_OK; } | < | 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 | JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r"); JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w"); JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w"); return JIM_OK; } #include <errno.h> #include <stdio.h> #include <string.h> #ifdef HAVE_DIRENT_H |
︙ | ︙ | |||
2495 2496 2497 2498 2499 2500 2501 | if (dirPtr == NULL) { if (nocomplain) { return JIM_OK; } Jim_SetResultString(interp, strerror(errno), -1); return JIM_ERR; } | < | | | | | | | | | | | < | | > > | > > > > > | 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 | if (dirPtr == NULL) { if (nocomplain) { return JIM_OK; } Jim_SetResultString(interp, strerror(errno), -1); return JIM_ERR; } else { Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); while ((entryPtr = readdir(dirPtr)) != NULL) { if (entryPtr->d_name[0] == '.') { if (entryPtr->d_name[1] == '\0') { continue; } if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0')) continue; } Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1)); } closedir(dirPtr); Jim_SetResult(interp, listObj); return JIM_OK; } } int Jim_readdirInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "readdir", "1.0", JIM_ERRMSG)) return JIM_ERR; Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL); return JIM_OK; } #include <stdlib.h> #include <string.h> #if defined(JIM_REGEXP) #else #include <regex.h> #endif static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) { regfree(objPtr->internalRep.regexpValue.compre); Jim_Free(objPtr->internalRep.regexpValue.compre); } |
︙ | ︙ | |||
3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif # ifndef MAXPATHLEN # define MAXPATHLEN JIM_PATH_LEN # endif static const char *JimGetFileType(int mode) { if (S_ISREG(mode)) { return "file"; } else if (S_ISDIR(mode)) { | > > > > > > | 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif # ifndef MAXPATHLEN # define MAXPATHLEN JIM_PATH_LEN # endif #if defined(__MINGW32__) || defined(_MSC_VER) #define ISWINDOWS 1 #else #define ISWINDOWS 0 #endif static const char *JimGetFileType(int mode) { if (S_ISREG(mode)) { return "file"; } else if (S_ISDIR(mode)) { |
︙ | ︙ | |||
3074 3075 3076 3077 3078 3079 3080 | else if (S_ISSOCK(mode)) { return "socket"; } #endif return "unknown"; } | | < < | < | < < < < < < < < < < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > | > | | | > | | | | | > | | > > | < | | > | < | < | 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 | else if (S_ISSOCK(mode)) { return "socket"; } #endif return "unknown"; } static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value) { Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1)); Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value)); } static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb) { Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); AppendStatElement(interp, listObj, "dev", sb->st_dev); AppendStatElement(interp, listObj, "ino", sb->st_ino); AppendStatElement(interp, listObj, "mode", sb->st_mode); AppendStatElement(interp, listObj, "nlink", sb->st_nlink); AppendStatElement(interp, listObj, "uid", sb->st_uid); AppendStatElement(interp, listObj, "gid", sb->st_gid); AppendStatElement(interp, listObj, "size", sb->st_size); AppendStatElement(interp, listObj, "atime", sb->st_atime); AppendStatElement(interp, listObj, "mtime", sb->st_mtime); AppendStatElement(interp, listObj, "ctime", sb->st_ctime); Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1)); Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1)); if (varName) { Jim_Obj *objPtr = Jim_GetVariable(interp, varName, JIM_NONE); if (objPtr) { if (Jim_DictSize(interp, objPtr) < 0) { Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName); Jim_FreeNewObj(interp, listObj); return JIM_ERR; } if (Jim_IsShared(objPtr)) objPtr = Jim_DuplicateObj(interp, objPtr); Jim_ListAppendList(interp, objPtr, listObj); Jim_DictSize(interp, objPtr); Jim_InvalidateStringRep(objPtr); Jim_FreeNewObj(interp, listObj); listObj = objPtr; } Jim_SetVariable(interp, varName, listObj); } Jim_SetResult(interp, listObj); return JIM_OK; } static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *path = Jim_String(argv[0]); const char *p = strrchr(path, '/'); if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') { Jim_SetResultString(interp, "..", -1); } else if (!p) { Jim_SetResultString(interp, ".", -1); } else if (p == path) { Jim_SetResultString(interp, "/", -1); } else if (ISWINDOWS && p[-1] == ':') { Jim_SetResultString(interp, path, p - path + 1); } else { Jim_SetResultString(interp, path, p - path); } return JIM_OK; } static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) |
︙ | ︙ | |||
3231 3232 3233 3234 3235 3236 3237 | int len; const char *part = Jim_GetString(argv[i], &len); if (*part == '/') { last = newname; } | < | < | 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 | int len; const char *part = Jim_GetString(argv[i], &len); if (*part == '/') { last = newname; } else if (ISWINDOWS && strchr(part, ':')) { last = newname; } else if (part[0] == '.') { if (part[1] == '/') { part += 2; len -= 2; } else if (part[1] == 0 && last != newname) { |
︙ | ︙ | |||
3265 3266 3267 3268 3269 3270 3271 | } memcpy(last, part, len); last += len; } if (last > newname + 1 && last[-1] == '/') { | > > | > < < < | > | 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 | } memcpy(last, part, len); last += len; } if (last > newname + 1 && last[-1] == '/') { if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) { *--last = 0; } } } *last = 0; Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname)); return JIM_OK; } static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode) { Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1); return JIM_OK; } static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return file_access(interp, argv[0], R_OK); } static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return file_access(interp, argv[0], W_OK); } static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { #ifdef X_OK return file_access(interp, argv[0], X_OK); #else Jim_SetResultBool(interp, 1); return JIM_OK; #endif } static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { |
︙ | ︙ | |||
3407 3408 3409 3410 3411 3412 3413 | return JIM_ERR; } argv++; } return JIM_OK; } | < < < < | < < | < < < < < < | 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 | return JIM_ERR; } argv++; } return JIM_OK; } static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL); if (fd < 0) { return JIM_ERR; } close(fd); return JIM_OK; } static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *source; const char *dest; int force = 0; |
︙ | ︙ | |||
3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 | Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1], strerror(errno)); return JIM_ERR; } return JIM_OK; } static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb) { const char *path = Jim_String(filename); if (stat(path, sb) == -1) { Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno)); return JIM_ERR; } return JIM_OK; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < > > > | 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 | Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1], strerror(errno)); return JIM_ERR; } return JIM_OK; } #if defined(HAVE_LINK) && defined(HAVE_SYMLINK) static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int ret; const char *source; const char *dest; static const char * const options[] = { "-hard", "-symbolic", NULL }; enum { OPT_HARD, OPT_SYMBOLIC, }; int option = OPT_HARD; if (argc == 3) { if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) { return JIM_ERR; } argv++; argc--; } dest = Jim_String(argv[0]); source = Jim_String(argv[1]); if (option == OPT_HARD) { ret = link(source, dest); } else { ret = symlink(source, dest); } if (ret != 0) { Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1], strerror(errno)); return JIM_ERR; } return JIM_OK; } #endif static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb) { const char *path = Jim_String(filename); if (stat(path, sb) == -1) { Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno)); return JIM_ERR; } return JIM_OK; } #ifdef HAVE_LSTAT static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb) { const char *path = Jim_String(filename); if (lstat(path, sb) == -1) { Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno)); return JIM_ERR; } return JIM_OK; } #else #define file_lstat file_stat #endif static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { struct stat sb; if (file_stat(interp, argv[0], &sb) != JIM_OK) { return JIM_ERR; |
︙ | ︙ | |||
3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 | if (file_lstat(interp, argv[0], &sb) != JIM_OK) { return JIM_ERR; } Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1); return JIM_OK; } static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { struct stat sb; if (file_lstat(interp, argv[0], &sb) != JIM_OK) { return JIM_ERR; } | > | > > > | | 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 | if (file_lstat(interp, argv[0], &sb) != JIM_OK) { return JIM_ERR; } Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1); return JIM_OK; } #ifdef HAVE_LSTAT static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { struct stat sb; if (file_lstat(interp, argv[0], &sb) != JIM_OK) { return JIM_ERR; } return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb); } #else #define file_cmd_lstat file_cmd_stat #endif static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { struct stat sb; if (file_stat(interp, argv[0], &sb) != JIM_OK) { return JIM_ERR; } return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb); } static const jim_subcmd_type file_command_table[] = { { "atime", "name", file_cmd_atime, 1, |
︙ | ︙ | |||
3743 3744 3745 3746 3747 3748 3749 | { "mkdir", "dir ...", file_cmd_mkdir, 1, -1, }, | < < > > > > > > > > > | < > | < > | 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 | { "mkdir", "dir ...", file_cmd_mkdir, 1, -1, }, { "tempfile", "?template?", file_cmd_tempfile, 0, 1, }, { "rename", "?-force? source dest", file_cmd_rename, 2, 3, }, #if defined(HAVE_LINK) && defined(HAVE_SYMLINK) { "link", "?-symbolic|-hard? newname target", file_cmd_link, 2, 3, }, #endif #if defined(HAVE_READLINK) { "readlink", "name", file_cmd_readlink, 1, 1, }, #endif { "size", "name", file_cmd_size, 1, 1, }, { "stat", "name ?var?", file_cmd_stat, 1, 2, }, { "lstat", "name ?var?", file_cmd_lstat, 1, 2, }, { "type", "name", file_cmd_type, 1, |
︙ | ︙ | |||
3845 3846 3847 3848 3849 3850 3851 | return JIM_ERR; } return JIM_OK; } static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { | < | | > | < < | | 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 | return JIM_ERR; } return JIM_OK; } static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { char *cwd = Jim_Alloc(MAXPATHLEN); if (getcwd(cwd, MAXPATHLEN) == NULL) { Jim_SetResultString(interp, "Failed to get pwd", -1); Jim_Free(cwd); return JIM_ERR; } else if (ISWINDOWS) { char *p = cwd; while ((p = strchr(p, '\\')) != NULL) { *p++ = '/'; } } Jim_SetResultString(interp, cwd, -1); Jim_Free(cwd); return JIM_OK; } int Jim_fileInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG)) return JIM_ERR; |
︙ | ︙ | |||
3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 | return JIM_OK; } int Jim_execInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG)) return JIM_ERR; Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL); return JIM_OK; } #else #include <errno.h> | > | 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 | return JIM_OK; } int Jim_execInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG)) return JIM_ERR; Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL); return JIM_OK; } #else #include <errno.h> |
︙ | ︙ | |||
3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 | static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId); static int JimErrno(void); #else #include <unistd.h> #include <fcntl.h> #include <sys/wait.h> typedef int fdtype; typedef int pidtype; #define JimPipe pipe #define JimErrno() errno #define JIM_BAD_FD -1 #define JIM_BAD_PID -1 | > | 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 | static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId); static int JimErrno(void); #else #include <unistd.h> #include <fcntl.h> #include <sys/wait.h> #include <sys/stat.h> typedef int fdtype; typedef int pidtype; #define JimPipe pipe #define JimErrno() errno #define JIM_BAD_FD -1 #define JIM_BAD_PID -1 |
︙ | ︙ | |||
4003 4004 4005 4006 4007 4008 4009 | static const char *JimStrError(void); static char **JimSaveEnv(char **env); static void JimRestoreEnv(char **env); static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr); static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr); static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId); | | | 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 | static const char *JimStrError(void); static char **JimSaveEnv(char **env); static void JimRestoreEnv(char **env); static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr); static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr); static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId); static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents, int len); static fdtype JimOpenForWrite(const char *filename, int append); static int JimRewindFd(fdtype fd); static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg) { Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError()); } |
︙ | ︙ | |||
4050 4051 4052 4053 4054 4055 4056 | } } Jim_RemoveTrailingNewline(strObj); fclose(fh); return JIM_OK; } | < < < < < < < < < < < > | 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 | } } Jim_RemoveTrailingNewline(strObj); fclose(fh); return JIM_OK; } static char **JimBuildEnv(Jim_Interp *interp) { int i; int size; int num; int n; char **envptr; char *envdata; Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE); if (!objPtr) { return Jim_GetEnviron(); } num = Jim_ListLength(interp, objPtr); if (num % 2) { num--; } size = Jim_Length(objPtr) + 2; envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size); envdata = (char *)&envptr[num / 2 + 1]; |
︙ | ︙ | |||
4106 4107 4108 4109 4110 4111 4112 | envdata++; n++; } envptr[n] = NULL; *envdata = 0; return envptr; | < < < < < | 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 | envdata++; n++; } envptr[n] = NULL; *envdata = 0; return envptr; } static void JimFreeEnv(char **env, char **original_environ) { if (env != original_environ) { Jim_Free(env); } } static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus) { Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); int rc = JIM_ERR; |
︙ | ︙ | |||
4170 4171 4172 4173 4174 4175 4176 | Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); return rc; } struct WaitInfo { | | | | | | 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 | Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); return rc; } struct WaitInfo { pidtype pid; int status; int flags; }; struct WaitInfoTable { struct WaitInfo *info; int size; int used; }; #define WI_DETACHED 2 #define WAIT_TABLE_GROW_BY 4 |
︙ | ︙ | |||
4205 4206 4207 4208 4209 4210 4211 | table->size = table->used = 0; return table; } static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { | | < | < | 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 | table->size = table->used = 0; return table; } static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { fdtype outputId; fdtype errorId; pidtype *pidPtr; int numPids, result; if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) { Jim_Obj *listObj; int i; |
︙ | ︙ | |||
4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 | return result; } static void JimReapDetachedPids(struct WaitInfoTable *table) { struct WaitInfo *waitPtr; int count; if (!table) { return; } | > > > | | < < | > > > > > | 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 | return result; } static void JimReapDetachedPids(struct WaitInfoTable *table) { struct WaitInfo *waitPtr; int count; int dest; if (!table) { return; } waitPtr = table->info; dest = 0; for (count = table->used; count > 0; waitPtr++, count--) { if (waitPtr->flags & WI_DETACHED) { int status; pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG); if (pid == waitPtr->pid) { table->used--; continue; } } if (waitPtr != &table->info[dest]) { table->info[dest] = *waitPtr; } dest++; } } static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr) { int i; |
︙ | ︙ | |||
4300 4301 4302 4303 4304 4305 4306 | return pid; } } return JIM_BAD_PID; } | < | 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 | return pid; } } return JIM_BAD_PID; } static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr) { int j; struct WaitInfoTable *table = Jim_CmdPrivData(interp); for (j = 0; j < numPids; j++) { |
︙ | ︙ | |||
4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 | int numPids = 0; /* Actual number of processes that exist * at *pidPtr right now. */ int cmdCount; /* Count of number of distinct commands * found in argc/argv. */ const char *input = NULL; /* Describes input for pipeline, depending * on "inputFile". NULL means take input * from stdin/pipe. */ #define FILE_NAME 0 #define FILE_APPEND 1 #define FILE_HANDLE 2 #define FILE_TEXT 3 int inputFile = FILE_NAME; /* 1 means input is name of input file. | > | 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 | int numPids = 0; /* Actual number of processes that exist * at *pidPtr right now. */ int cmdCount; /* Count of number of distinct commands * found in argc/argv. */ const char *input = NULL; /* Describes input for pipeline, depending * on "inputFile". NULL means take input * from stdin/pipe. */ int input_len = 0; #define FILE_NAME 0 #define FILE_APPEND 1 #define FILE_HANDLE 2 #define FILE_TEXT 3 int inputFile = FILE_NAME; /* 1 means input is name of input file. |
︙ | ︙ | |||
4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 | const char *arg = Jim_String(argv[i]); if (arg[0] == '<') { inputFile = FILE_NAME; input = arg + 1; if (*input == '<') { inputFile = FILE_TEXT; input++; } else if (*input == '@') { inputFile = FILE_HANDLE; input++; } if (!*input && ++i < argc) { | > | | 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 | const char *arg = Jim_String(argv[i]); if (arg[0] == '<') { inputFile = FILE_NAME; input = arg + 1; if (*input == '<') { inputFile = FILE_TEXT; input_len = Jim_Length(argv[i]) - 2; input++; } else if (*input == '@') { inputFile = FILE_HANDLE; input++; } if (!*input && ++i < argc) { input = Jim_GetString(argv[i], &input_len); } } else if (arg[0] == '>') { int dup_error = 0; outputFile = FILE_NAME; |
︙ | ︙ | |||
4496 4497 4498 4499 4500 4501 4502 | } save_environ = JimSaveEnv(JimBuildEnv(interp)); if (input != NULL) { if (inputFile == FILE_TEXT) { | | | 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 | } save_environ = JimSaveEnv(JimBuildEnv(interp)); if (input != NULL) { if (inputFile == FILE_TEXT) { inputId = JimCreateTemp(interp, input, input_len); if (inputId == JIM_BAD_FD) { goto error; } } else if (inputFile == FILE_HANDLE) { FILE *fh = JimGetAioFilehandle(interp, input); |
︙ | ︙ | |||
4585 4586 4587 4588 4589 4590 4591 | if (errorId == JIM_BAD_FD) { Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError()); goto error; } } } else if (errFilePtr != NULL) { | | | 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 | if (errorId == JIM_BAD_FD) { Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError()); goto error; } } } else if (errFilePtr != NULL) { errorId = JimCreateTemp(interp, NULL, 0); if (errorId == JIM_BAD_FD) { goto error; } *errFilePtr = JimDupFd(errorId); } |
︙ | ︙ | |||
4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 | Jim_SetResultErrno(interp, "couldn't create pipe"); goto error; } outputId = pipeIds[1]; } #ifdef __MINGW32__ pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId); if (pid == JIM_BAD_PID) { Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]); goto error; } #else | > > > > > < < < < < < < < < > > > | | 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 | Jim_SetResultErrno(interp, "couldn't create pipe"); goto error; } outputId = pipeIds[1]; } if (pipe_dup_err) { errorId = outputId; } #ifdef __MINGW32__ pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId); if (pid == JIM_BAD_PID) { Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]); goto error; } #else pid = vfork(); if (pid < 0) { Jim_SetResultErrno(interp, "couldn't fork child process"); goto error; } if (pid == 0) { if (inputId != -1) dup2(inputId, 0); if (outputId != -1) dup2(outputId, 1); if (errorId != -1) dup2(errorId, 2); for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) { close(i); } (void)signal(SIGPIPE, SIG_DFL); execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron()); fprintf(stderr, "couldn't exec \"%s\"\n", arg_array[firstArg]); _exit(127); } #endif if (table->used == table->size) { |
︙ | ︙ | |||
4765 4766 4767 4768 4769 4770 4771 | if (errorId != JIM_BAD_FD) { JimRewindFd(errorId); if (JimAppendStreamToString(interp, errorId, Jim_GetResult(interp)) != JIM_OK) { result = JIM_ERR; } } | | > > > > > | 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 | if (errorId != JIM_BAD_FD) { JimRewindFd(errorId); if (JimAppendStreamToString(interp, errorId, Jim_GetResult(interp)) != JIM_OK) { result = JIM_ERR; } } Jim_RemoveTrailingNewline(Jim_GetResult(interp)); return result; } int Jim_execInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG)) return JIM_ERR; #ifdef SIGPIPE (void)signal(SIGPIPE, SIG_IGN); #endif Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable); return JIM_OK; } #if defined(__MINGW32__) |
︙ | ︙ | |||
4951 4952 4953 4954 4955 4956 4957 | } GetExitCodeProcess(pid, &ret); *status = ret; CloseHandle(pid); return pid; } | | | 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 | } GetExitCodeProcess(pid, &ret); *status = ret; CloseHandle(pid); return pid; } static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents, int len) { char name[MAX_PATH]; HANDLE handle; if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) { return JIM_BAD_FD; } |
︙ | ︙ | |||
4975 4976 4977 4978 4979 4980 4981 | if (contents != NULL) { FILE *fh = JimFdOpenForWrite(JimDupFd(handle)); if (fh == NULL) { goto error; } | | | 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 | if (contents != NULL) { FILE *fh = JimFdOpenForWrite(JimDupFd(handle)); if (fh == NULL) { goto error; } if (fwrite(contents, len, 1, fh) != 1) { fclose(fh); goto error; } fseek(fh, 0, SEEK_SET); fclose(fh); } return handle; |
︙ | ︙ | |||
5102 5103 5104 5105 5106 5107 5108 | static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId) { STARTUPINFO startInfo; PROCESS_INFORMATION procInfo; HANDLE hProcess, h; char execPath[MAX_PATH]; | < < | | 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 | static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId) { STARTUPINFO startInfo; PROCESS_INFORMATION procInfo; HANDLE hProcess, h; char execPath[MAX_PATH]; pidtype pid = JIM_BAD_PID; Jim_Obj *cmdLineObj; if (JimWinFindExecutable(argv[0], execPath) < 0) { return JIM_BAD_PID; } argv[0] = execPath; hProcess = GetCurrentProcess(); cmdLineObj = JimWinBuildCommandLine(interp, argv); ZeroMemory(&startInfo, sizeof(startInfo)); startInfo.cb = sizeof(startInfo); startInfo.dwFlags = STARTF_USESTDHANDLES; startInfo.hStdInput = INVALID_HANDLE_VALUE; startInfo.hStdOutput= INVALID_HANDLE_VALUE; startInfo.hStdError = INVALID_HANDLE_VALUE; if (inputId == JIM_BAD_FD) { if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) { CloseHandle(h); } |
︙ | ︙ | |||
5194 5195 5196 5197 5198 5199 5200 | } static int JimRewindFd(int fd) { return lseek(fd, 0L, SEEK_SET); } | | | < | < < < | | < | | | | | | > < | 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 | } static int JimRewindFd(int fd) { return lseek(fd, 0L, SEEK_SET); } static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len) { int fd = Jim_MakeTempFile(interp, NULL); if (fd != JIM_BAD_FD) { unlink(Jim_String(Jim_GetResult(interp))); if (contents) { if (write(fd, contents, len) != len) { Jim_SetResultErrno(interp, "couldn't write temp file"); close(fd); return -1; } lseek(fd, 0L, SEEK_SET); } } return fd; } static char **JimSaveEnv(char **env) { char **saveenv = Jim_GetEnviron(); Jim_SetEnviron(env); return saveenv; } static void JimRestoreEnv(char **env) { JimFreeEnv(Jim_GetEnviron(), env); Jim_SetEnviron(env); } #endif #endif #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 500 #endif #include <stdlib.h> |
︙ | ︙ | |||
5254 5255 5256 5257 5258 5259 5260 | static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { char buf[100]; time_t t; long seconds; | | | > > > | 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 | static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { char buf[100]; time_t t; long seconds; const char *format = "%a %b %d %H:%M:%S %Z %Y"; if (argc == 2 || (argc == 3 && !Jim_CompareStringImmediate(interp, argv[1], "-format"))) { return -1; } if (argc == 3) { format = Jim_String(argv[2]); } if (Jim_GetLong(interp, argv[0], &seconds) != JIM_OK) { return JIM_ERR; } t = seconds; if (strftime(buf, sizeof(buf), format, localtime(&t)) == 0) { Jim_SetResultString(interp, "format string too long", -1); return JIM_ERR; } Jim_SetResultString(interp, buf, -1); return JIM_OK; } #ifdef HAVE_STRPTIME |
︙ | ︙ | |||
5389 5390 5391 5392 5393 5394 5395 | if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG)) return JIM_ERR; Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL); return JIM_OK; } | < > < | < < | < | < < | < > | | | | | | > | | 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 | if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG)) return JIM_ERR; Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL); return JIM_OK; } #include <limits.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <errno.h> static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_SetResultInt(interp, Jim_GetVariable(interp, argv[0], 0) != 0); return JIM_OK; } static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); Jim_Obj *patternObj; if (!objPtr) { return JIM_OK; } patternObj = (argc == 1) ? NULL : argv[1]; if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) { if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) { Jim_SetResult(interp, objPtr); return JIM_OK; } } return Jim_DictValues(interp, objPtr, patternObj); } static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); if (!objPtr) { |
︙ | ︙ | |||
5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 | if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) { Jim_UnsetVariable(interp, argv[0], JIM_NONE); return JIM_OK; } objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) { return JIM_ERR; } resultObj = Jim_NewDictObj(interp, NULL, 0); | > > > > > | 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 | if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) { Jim_UnsetVariable(interp, argv[0], JIM_NONE); return JIM_OK; } objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); if (objPtr == NULL) { return JIM_OK; } if (Jim_DictPairs(interp, objPtr, &dictValuesObj, &len) != JIM_OK) { return JIM_ERR; } resultObj = Jim_NewDictObj(interp, NULL, 0); |
︙ | ︙ | |||
5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 | } } Jim_SetResultInt(interp, len); return JIM_OK; } static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int i; int len; Jim_Obj *listObj = argv[1]; Jim_Obj *dictObj; len = Jim_ListLength(interp, listObj); if (len % 2) { Jim_SetResultString(interp, "list must have an even number of elements", -1); return JIM_ERR; } dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED); if (!dictObj) { return Jim_SetVariable(interp, argv[0], listObj); } if (Jim_IsShared(dictObj)) { dictObj = Jim_DuplicateObj(interp, dictObj); } for (i = 0; i < len; i += 2) { | > > > > > > > > > > > > > | 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 | } } Jim_SetResultInt(interp, len); return JIM_OK; } static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); if (objPtr) { return Jim_DictInfo(interp, objPtr); } Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL); return JIM_ERR; } static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int i; int len; Jim_Obj *listObj = argv[1]; Jim_Obj *dictObj; len = Jim_ListLength(interp, listObj); if (len % 2) { Jim_SetResultString(interp, "list must have an even number of elements", -1); return JIM_ERR; } dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED); if (!dictObj) { return Jim_SetVariable(interp, argv[0], listObj); } else if (Jim_DictSize(interp, dictObj) < 0) { return JIM_ERR; } if (Jim_IsShared(dictObj)) { dictObj = Jim_DuplicateObj(interp, dictObj); } for (i = 0; i < len; i += 2) { |
︙ | ︙ | |||
5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 | }, { "size", "arrayName", array_cmd_size, 1, 1, }, { "unset", "arrayName ?pattern?", array_cmd_unset, 1, 2, | > > > > > > > | 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 | }, { "size", "arrayName", array_cmd_size, 1, 1, }, { "stat", "arrayName", array_cmd_stat, 1, 1, }, { "unset", "arrayName ?pattern?", array_cmd_unset, 1, 2, |
︙ | ︙ | |||
5612 5613 5614 5615 5616 5617 5618 | Jim_execInit(interp); Jim_clockInit(interp); Jim_arrayInit(interp); Jim_stdlibInit(interp); Jim_tclcompatInit(interp); return JIM_OK; } | < | 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 | Jim_execInit(interp); Jim_clockInit(interp); Jim_arrayInit(interp); Jim_stdlibInit(interp); Jim_tclcompatInit(interp); return JIM_OK; } #define JIM_OPTIMIZATION #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> |
︙ | ︙ | |||
5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 | #ifdef JIM_MAINTAINER #define JIM_DEBUG_COMMAND #define JIM_DEBUG_PANIC #endif #define JIM_INTEGER_SPACE 24 const char *jim_tt_name(int type); #ifdef JIM_DEBUG_PANIC | > | < | | 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 | #ifdef JIM_MAINTAINER #define JIM_DEBUG_COMMAND #define JIM_DEBUG_PANIC #endif #define JIM_INTEGER_SPACE 24 const char *jim_tt_name(int type); #ifdef JIM_DEBUG_PANIC static void JimPanicDump(int fail_condition, const char *fmt, ...); #define JimPanic(X) JimPanicDump X #else #define JimPanic(X) #endif static char JimEmptyStringRep[] = ""; static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action); static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr, int flags); static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands); static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr); static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr); static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len); static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, |
︙ | ︙ | |||
5936 5937 5938 5939 5940 5941 5942 | if (n > 0) { n = utf8_strlen(s2, n); } return n; } #endif | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 | if (n > 0) { n = utf8_strlen(s2, n); } return n; } #endif static int JimCheckConversion(const char *str, const char *endptr) { if (str[0] == '\0' || str == endptr) { return JIM_ERR; } if (endptr[0] != '\0') { |
︙ | ︙ | |||
6027 6028 6029 6030 6031 6032 6033 | i += 2; if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) { return i; } | > | | 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 | i += 2; if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) { return i; } *base = 10; return 0; } static long jim_strtol(const char *str, char **endptr) { int sign; int base; int i = JimNumberBase(str, &base, &sign); |
︙ | ︙ | |||
6083 6084 6085 6086 6087 6088 6089 | else { *widePtr = jim_strtoull(str, &endptr); } return JimCheckConversion(str, endptr); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 | else { *widePtr = jim_strtoull(str, &endptr); } return JimCheckConversion(str, endptr); } int Jim_StringToDouble(const char *str, double *doublePtr) { char *endptr; errno = 0; |
︙ | ︙ | |||
6144 6145 6146 6147 6148 6149 6150 | for (i = 0; i < e; i++) { res *= b; } return res; } #ifdef JIM_DEBUG_PANIC | | | | | | | | 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 | for (i = 0; i < e; i++) { res *= b; } return res; } #ifdef JIM_DEBUG_PANIC static void JimPanicDump(int condition, const char *fmt, ...) { va_list ap; if (!condition) { return; } va_start(ap, fmt); fprintf(stderr, "\nJIM INTERPRETER PANIC: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n\n"); va_end(ap); #ifdef HAVE_BACKTRACE { void *array[40]; int size, i; char **strings; size = backtrace(array, 40); strings = backtrace_symbols(array, size); for (i = 0; i < size; i++) fprintf(stderr, "[backtrace] %s\n", strings[i]); fprintf(stderr, "[backtrace] Include the above lines and the output\n"); fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n"); } #endif exit(1); } #endif |
︙ | ︙ | |||
6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 | while (len--) h += (h << 3) + *buf++; return h; } static void JimResetHashTable(Jim_HashTable *ht) { ht->table = NULL; ht->size = 0; ht->sizemask = 0; ht->used = 0; ht->collisions = 0; } static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter) { iter->ht = ht; iter->index = -1; iter->entry = NULL; | > > > > > > | 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 | while (len--) h += (h << 3) + *buf++; return h; } static void JimResetHashTable(Jim_HashTable *ht) { ht->table = NULL; ht->size = 0; ht->sizemask = 0; ht->used = 0; ht->collisions = 0; #ifdef JIM_RANDOMISE_HASH ht->uniq = (rand() ^ time(NULL) ^ clock()); #else ht->uniq = 0; #endif } static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter) { iter->ht = ht; iter->index = -1; iter->entry = NULL; |
︙ | ︙ | |||
6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 | if (size <= ht->used) return; Jim_InitHashTable(&n, ht->type, ht->privdata); n.size = realsize; n.sizemask = realsize - 1; n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *)); memset(n.table, 0, realsize * sizeof(Jim_HashEntry *)); n.used = ht->used; for (i = 0; ht->used > 0; i++) { Jim_HashEntry *he, *nextHe; | > > | 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 | if (size <= ht->used) return; Jim_InitHashTable(&n, ht->type, ht->privdata); n.size = realsize; n.sizemask = realsize - 1; n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *)); n.uniq = ht->uniq; memset(n.table, 0, realsize * sizeof(Jim_HashEntry *)); n.used = ht->used; for (i = 0; ht->used > 0; i++) { Jim_HashEntry *he, *nextHe; |
︙ | ︙ | |||
6353 6354 6355 6356 6357 6358 6359 | int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val) { int existed; Jim_HashEntry *entry; entry = JimInsertHashEntry(ht, key, 1); if (entry->key) { | > > > > | > | > > > < | 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 | int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val) { int existed; Jim_HashEntry *entry; entry = JimInsertHashEntry(ht, key, 1); if (entry->key) { if (ht->type->valDestructor && ht->type->valDup) { void *newval = ht->type->valDup(ht->privdata, val); ht->type->valDestructor(ht->privdata, entry->u.val); entry->u.val = newval; } else { Jim_FreeEntryVal(ht, entry); Jim_SetHashVal(ht, entry, val); } existed = 1; } else { Jim_SetHashKey(ht, entry, key); Jim_SetHashVal(ht, entry, val); existed = 0; } return existed; } int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key) { |
︙ | ︙ | |||
6531 6532 6533 6534 6535 6536 6537 | static unsigned int JimStringCopyHTHashFunction(const void *key) { return Jim_GenHashFunction(key, strlen(key)); } static void *JimStringCopyHTDup(void *privdata, const void *key) { | | | 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 | static unsigned int JimStringCopyHTHashFunction(const void *key) { return Jim_GenHashFunction(key, strlen(key)); } static void *JimStringCopyHTDup(void *privdata, const void *key) { return Jim_StrDup(key); } static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2) { return strcmp(key1, key2) == 0; } |
︙ | ︙ | |||
6631 6632 6633 6634 6635 6636 6637 | for (i = 0; i < stack->len; i++) freeFunc(stack->vector[i]); } | | | 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 | for (i = 0; i < stack->len; i++) freeFunc(stack->vector[i]); } #define JIM_TT_NONE 0 #define JIM_TT_STR 1 #define JIM_TT_ESC 2 #define JIM_TT_VAR 3 #define JIM_TT_DICTSUGAR 4 #define JIM_TT_CMD 5 #define JIM_TT_SEP 6 |
︙ | ︙ | |||
6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 | #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF) #define JIM_PS_DEF 0 #define JIM_PS_QUOTE 1 #define JIM_PS_DICTSUGAR 2 struct JimParserCtx { const char *p; int len; int linenr; const char *tstart; const char *tend; int tline; int tt; int eof; int state; int comment; | > > > > > < < < < | < < < | | | 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 | #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF) #define JIM_PS_DEF 0 #define JIM_PS_QUOTE 1 #define JIM_PS_DICTSUGAR 2 struct JimParseMissing { int ch; int line; }; struct JimParserCtx { const char *p; int len; int linenr; const char *tstart; const char *tend; int tline; int tt; int eof; int state; int comment; struct JimParseMissing missing; }; static int JimParseScript(struct JimParserCtx *pc); static int JimParseSep(struct JimParserCtx *pc); static int JimParseEol(struct JimParserCtx *pc); static int JimParseCmd(struct JimParserCtx *pc); static int JimParseQuote(struct JimParserCtx *pc); static int JimParseVar(struct JimParserCtx *pc); static int JimParseBrace(struct JimParserCtx *pc); static int JimParseStr(struct JimParserCtx *pc); static int JimParseComment(struct JimParserCtx *pc); static void JimParseSubCmd(struct JimParserCtx *pc); static int JimParseSubQuote(struct JimParserCtx *pc); static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc); static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr) { pc->p = prg; pc->len = len; pc->tstart = NULL; pc->tend = NULL; pc->tline = 0; pc->tt = JIM_TT_NONE; pc->eof = 0; pc->state = JIM_PS_DEF; pc->linenr = linenr; pc->comment = 1; pc->missing.ch = ' '; pc->missing.line = linenr; } static int JimParseScript(struct JimParserCtx *pc) { while (1) { if (!pc->len) { pc->tstart = pc->p; |
︙ | ︙ | |||
6848 6849 6850 6851 6852 6853 6854 | case '\n': pc->linenr++; break; } pc->p++; pc->len--; } | | | | 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 | case '\n': pc->linenr++; break; } pc->p++; pc->len--; } pc->missing.ch = '{'; pc->missing.line = pc->tline; pc->tend = pc->p - 1; } static int JimParseSubQuote(struct JimParserCtx *pc) { int tt = JIM_TT_STR; int line = pc->tline; |
︙ | ︙ | |||
6895 6896 6897 6898 6899 6900 6901 | case '$': tt = JIM_TT_ESC; break; } pc->p++; pc->len--; } | | | | 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 | case '$': tt = JIM_TT_ESC; break; } pc->p++; pc->len--; } pc->missing.ch = '"'; pc->missing.line = line; pc->tend = pc->p - 1; return tt; } static void JimParseSubCmd(struct JimParserCtx *pc) { int level = 1; |
︙ | ︙ | |||
6954 6955 6956 6957 6958 6959 6960 | pc->linenr++; break; } startofword = isspace(UCHAR(*pc->p)); pc->p++; pc->len--; } | | | | 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 | pc->linenr++; break; } startofword = isspace(UCHAR(*pc->p)); pc->p++; pc->len--; } pc->missing.ch = '['; pc->missing.line = line; pc->tend = pc->p - 1; } static int JimParseBrace(struct JimParserCtx *pc) { pc->tstart = pc->p + 1; pc->tline = pc->linenr; |
︙ | ︙ | |||
7099 7100 7101 7102 7103 7104 7105 | return JimParseBrace(pc); } if (*pc->p == '"') { pc->state = JIM_PS_QUOTE; pc->p++; pc->len--; | | | > > > > | 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 | return JimParseBrace(pc); } if (*pc->p == '"') { pc->state = JIM_PS_QUOTE; pc->p++; pc->len--; pc->missing.line = pc->tline; } } pc->tstart = pc->p; pc->tline = pc->linenr; while (1) { if (pc->len == 0) { if (pc->state == JIM_PS_QUOTE) { pc->missing.ch = '"'; } pc->tend = pc->p - 1; pc->tt = JIM_TT_ESC; return JIM_OK; } switch (*pc->p) { case '\\': if (pc->state == JIM_PS_DEF && *(pc->p + 1) == '\n') { pc->tend = pc->p - 1; pc->tt = JIM_TT_ESC; return JIM_OK; } if (pc->len >= 2) { if (*(pc->p + 1) == '\n') { pc->linenr++; } pc->p++; pc->len--; } else if (pc->len == 1) { pc->missing.ch = '\\'; } break; case '(': if (pc->len > 1 && pc->p[1] != '$') { break; } case ')': |
︙ | ︙ | |||
7187 7188 7189 7190 7191 7192 7193 | } return JIM_OK; } static int JimParseComment(struct JimParserCtx *pc) { while (*pc->p) { | | < < | | > > > > > > > > > > > | 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 | } return JIM_OK; } static int JimParseComment(struct JimParserCtx *pc) { while (*pc->p) { if (*pc->p == '\\') { pc->p++; pc->len--; if (pc->len == 0) { pc->missing.ch = '\\'; return JIM_OK; } if (*pc->p == '\n') { pc->linenr++; } } else if (*pc->p == '\n') { pc->p++; pc->len--; pc->linenr++; break; } pc->p++; pc->len--; } return JIM_OK; } |
︙ | ︙ | |||
7415 7416 7417 7418 7419 7420 7421 | struct JimParserCtx parser; JimParserInit(&parser, s, len, 1); while (!parser.eof) { JimParseScript(&parser); } if (stateCharPtr) { | | | | 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 | struct JimParserCtx parser; JimParserInit(&parser, s, len, 1); while (!parser.eof) { JimParseScript(&parser); } if (stateCharPtr) { *stateCharPtr = parser.missing.ch; } return parser.missing.ch == ' '; } static int JimParseListSep(struct JimParserCtx *pc); static int JimParseListStr(struct JimParserCtx *pc); static int JimParseListQuote(struct JimParserCtx *pc); static int JimParseList(struct JimParserCtx *pc) |
︙ | ︙ | |||
7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 | if (objPtr->prevObjPtr) objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr; if (objPtr->nextObjPtr) objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr; if (interp->liveList == objPtr) interp->liveList = objPtr->nextObjPtr; objPtr->prevObjPtr = NULL; objPtr->nextObjPtr = interp->freeList; if (interp->freeList) interp->freeList->prevObjPtr = objPtr; interp->freeList = objPtr; objPtr->refCount = -1; } void Jim_InvalidateStringRep(Jim_Obj *objPtr) { if (objPtr->bytes != NULL) { if (objPtr->bytes != JimEmptyStringRep) | > > > > | 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 | if (objPtr->prevObjPtr) objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr; if (objPtr->nextObjPtr) objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr; if (interp->liveList == objPtr) interp->liveList = objPtr->nextObjPtr; #ifdef JIM_DISABLE_OBJECT_POOL Jim_Free(objPtr); #else objPtr->prevObjPtr = NULL; objPtr->nextObjPtr = interp->freeList; if (interp->freeList) interp->freeList->prevObjPtr = objPtr; interp->freeList = objPtr; objPtr->refCount = -1; #endif } void Jim_InvalidateStringRep(Jim_Obj *objPtr) { if (objPtr->bytes != NULL) { if (objPtr->bytes != JimEmptyStringRep) |
︙ | ︙ | |||
7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 | } const char *Jim_String(Jim_Obj *objPtr) { if (objPtr->bytes == NULL) { JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); objPtr->typePtr->updateStringProc(objPtr); } return objPtr->bytes; } static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static const Jim_ObjType dictSubstObjType = { "dict-substitution", FreeDictSubstInternalRep, | > > > > > > > | 7835 7836 7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 | } const char *Jim_String(Jim_Obj *objPtr) { if (objPtr->bytes == NULL) { JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value.")); JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); objPtr->typePtr->updateStringProc(objPtr); } return objPtr->bytes; } static void JimSetStringBytes(Jim_Obj *objPtr, const char *str) { objPtr->bytes = Jim_StrDup(str); objPtr->length = strlen(str); } static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static const Jim_ObjType dictSubstObjType = { "dict-substitution", FreeDictSubstInternalRep, |
︙ | ︙ | |||
7710 7711 7712 7713 7714 7715 7716 | }; static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { JIM_NOTUSED(interp); dupPtr->internalRep.strValue.maxLength = srcPtr->length; | < | 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 | }; static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { JIM_NOTUSED(interp); dupPtr->internalRep.strValue.maxLength = srcPtr->length; dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength; } static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { if (objPtr->typePtr != &stringObjType) { |
︙ | ︙ | |||
7759 7760 7761 7762 7763 7764 7765 | if (len == -1) len = strlen(s); if (len == 0) { objPtr->bytes = JimEmptyStringRep; | < < > | 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 | if (len == -1) len = strlen(s); if (len == 0) { objPtr->bytes = JimEmptyStringRep; } else { objPtr->bytes = Jim_Alloc(len + 1); memcpy(objPtr->bytes, s, len); objPtr->bytes[len] = '\0'; } objPtr->length = len; objPtr->typePtr = NULL; return objPtr; } |
︙ | ︙ | |||
7798 7799 7800 7801 7802 7803 7804 | } Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len) { Jim_Obj *objPtr = Jim_NewObj(interp); objPtr->bytes = s; | | | 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 | } Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len) { Jim_Obj *objPtr = Jim_NewObj(interp); objPtr->bytes = s; objPtr->length = (len == -1) ? strlen(s) : len; objPtr->typePtr = NULL; return objPtr; } static void StringAppendString(Jim_Obj *objPtr, const char *str, int len) { int needlen; |
︙ | ︙ | |||
7827 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 7838 7839 | else { objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1); } objPtr->internalRep.strValue.maxLength = needlen; } memcpy(objPtr->bytes + objPtr->length, str, len); objPtr->bytes[objPtr->length + len] = '\0'; if (objPtr->internalRep.strValue.charLength >= 0) { objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len); } objPtr->length += len; } | > < < < | | < < < | > > > | | | | < > | 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 8054 8055 8056 8057 8058 8059 8060 8061 8062 8063 8064 8065 8066 | else { objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1); } objPtr->internalRep.strValue.maxLength = needlen; } memcpy(objPtr->bytes + objPtr->length, str, len); objPtr->bytes[objPtr->length + len] = '\0'; if (objPtr->internalRep.strValue.charLength >= 0) { objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len); } objPtr->length += len; } void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len) { JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object")); SetStringFromAny(interp, objPtr); StringAppendString(objPtr, str, len); } void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr) { int len; const char *str = Jim_GetString(appendObjPtr, &len); Jim_AppendString(interp, objPtr, str, len); } void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...) { va_list ap; SetStringFromAny(interp, objPtr); va_start(ap, objPtr); while (1) { const char *s = va_arg(ap, const char *); if (s == NULL) break; Jim_AppendString(interp, objPtr, s, -1); } va_end(ap); } int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr) { if (aObjPtr == bObjPtr) { return 1; } else { int Alen, Blen; const char *sA = Jim_GetString(aObjPtr, &Alen); const char *sB = Jim_GetString(bObjPtr, &Blen); return Alen == Blen && memcmp(sA, sB, Alen) == 0; } } int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase) { return JimGlobMatch(Jim_String(patternObjPtr), Jim_String(objPtr), nocase); } |
︙ | ︙ | |||
8043 8044 8045 8046 8047 8048 8049 | } static void JimStrCopyUpperLower(char *dest, const char *str, int uc) { while (*str) { int c; str += utf8_tounicode(str, &c); | | | 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 | } static void JimStrCopyUpperLower(char *dest, const char *str, int uc) { while (*str) { int c; str += utf8_tounicode(str, &c); dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c)); } *dest = 0; } static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr) { char *buf; |
︙ | ︙ | |||
8103 8104 8105 8106 8107 8108 8109 | } #ifdef JIM_UTF8 len *= 2; #endif buf = p = Jim_Alloc(len + 1); str += utf8_tounicode(str, &c); | | | 8277 8278 8279 8280 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 | } #ifdef JIM_UTF8 len *= 2; #endif buf = p = Jim_Alloc(len + 1); str += utf8_tounicode(str, &c); p += utf8_getchars(p, utf8_title(c)); JimStrCopyUpperLower(p, str, 0); return Jim_NewStringObjNoAlloc(interp, buf, -1); } static const char *utf8_memchr(const char *str, int len, int c) |
︙ | ︙ | |||
8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 | nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen); if (nontrim == NULL) { return Jim_NewEmptyStringObj(interp); } if (nontrim == strObjPtr->bytes + len) { return strObjPtr; } if (Jim_IsShared(strObjPtr)) { strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes)); } else { | > | 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 | nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen); if (nontrim == NULL) { return Jim_NewEmptyStringObj(interp); } if (nontrim == strObjPtr->bytes + len) { return strObjPtr; } if (Jim_IsShared(strObjPtr)) { strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes)); } else { |
︙ | ︙ | |||
8233 8234 8235 8236 8237 8238 8239 | { Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr); strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr); | > | < | > > > > > > > > > | 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 | { Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr); strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr); if (objPtr != strObjPtr && objPtr->refCount == 0) { Jim_FreeNewObj(interp, objPtr); } return strObjPtr; } #ifdef HAVE_ISASCII #define jim_isascii isascii #else static int jim_isascii(int c) { return !(c & ~0x7f); } #endif static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict) { static const char * const strclassnames[] = { "integer", "alpha", "alnum", "ascii", "digit", "double", "lower", "upper", "space", "xdigit", "control", "print", "graph", "punct", |
︙ | ︙ | |||
8268 8269 8270 8271 8272 8273 8274 | if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { return JIM_ERR; } str = Jim_GetString(strObjPtr, &len); if (len == 0) { | | | | | | | | > > | 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 | if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { return JIM_ERR; } str = Jim_GetString(strObjPtr, &len); if (len == 0) { Jim_SetResultBool(interp, !strict); return JIM_OK; } switch (strclass) { case STR_IS_INTEGER: { jim_wide w; Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK); return JIM_OK; } case STR_IS_DOUBLE: { double d; Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE); return JIM_OK; } case STR_IS_ALPHA: isclassfunc = isalpha; break; case STR_IS_ALNUM: isclassfunc = isalnum; break; case STR_IS_ASCII: isclassfunc = jim_isascii; break; case STR_IS_DIGIT: isclassfunc = isdigit; break; case STR_IS_LOWER: isclassfunc = islower; break; case STR_IS_UPPER: isclassfunc = isupper; break; case STR_IS_SPACE: isclassfunc = isspace; break; case STR_IS_XDIGIT: isclassfunc = isxdigit; break; case STR_IS_CONTROL: isclassfunc = iscntrl; break; case STR_IS_PRINT: isclassfunc = isprint; break; case STR_IS_GRAPH: isclassfunc = isgraph; break; case STR_IS_PUNCT: isclassfunc = ispunct; break; default: return JIM_ERR; } for (i = 0; i < len; i++) { if (!isclassfunc(str[i])) { Jim_SetResultBool(interp, 0); return JIM_OK; } } Jim_SetResultBool(interp, 1); return JIM_OK; } static const Jim_ObjType comparedStringObjType = { "compared-string", NULL, NULL, NULL, JIM_TYPE_REFERENCES, }; int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str) { if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) { return 1; } else { const char *objStr = Jim_String(objPtr); if (strcmp(str, objStr) != 0) return 0; if (objPtr->typePtr != &comparedStringObjType) { Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &comparedStringObjType; } objPtr->internalRep.ptr = (char *)str; return 1; } |
︙ | ︙ | |||
8377 8378 8379 8380 8381 8382 8383 | Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj); } static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *fileNameObj, int lineNumber) { JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object")); | | < < > < < | > < > < > > < > < | < | 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609 8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 8658 8659 8660 8661 8662 | Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj); } static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *fileNameObj, int lineNumber) { JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object")); JimPanic((objPtr->typePtr != NULL, "JimSetSourceInfo called with typed object")); Jim_IncrRefCount(fileNameObj); objPtr->internalRep.sourceValue.fileNameObj = fileNameObj; objPtr->internalRep.sourceValue.lineNumber = lineNumber; objPtr->typePtr = &sourceObjType; } static const Jim_ObjType scriptLineObjType = { "scriptline", NULL, NULL, NULL, JIM_NONE, }; static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line) { Jim_Obj *objPtr; #ifdef DEBUG_SHOW_SCRIPT char buf[100]; snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc); objPtr = Jim_NewStringObj(interp, buf, -1); #else objPtr = Jim_NewEmptyStringObj(interp); #endif objPtr->typePtr = &scriptLineObjType; objPtr->internalRep.scriptLineValue.argc = argc; objPtr->internalRep.scriptLineValue.line = line; return objPtr; } static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); static int JimParseCheckMissing(Jim_Interp *interp, int ch); static const Jim_ObjType scriptObjType = { "script", FreeScriptInternalRep, DupScriptInternalRep, NULL, JIM_TYPE_REFERENCES, }; typedef struct ScriptToken { Jim_Obj *objPtr; int type; } ScriptToken; typedef struct ScriptObj { ScriptToken *token; Jim_Obj *fileNameObj; int len; int substFlags; int inUse; /* Used to share a ScriptObj. Currently only used by Jim_EvalObj() as protection against shimmering of the currently evaluated object. */ int firstline; int linenr; int missing; } ScriptObj; void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) { int i; struct ScriptObj *script = (void *)objPtr->internalRep.ptr; if (--script->inUse != 0) return; for (i = 0; i < script->len; i++) { Jim_DecrRefCount(interp, script->token[i].objPtr); } Jim_Free(script->token); Jim_DecrRefCount(interp, script->fileNameObj); Jim_Free(script); } void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { JIM_NOTUSED(interp); JIM_NOTUSED(srcPtr); dupPtr->typePtr = NULL; } typedef struct { const char *token; int len; |
︙ | ︙ | |||
8664 8665 8666 8667 8668 8669 8670 | if (lineargs == 0) { token--; } script->len = token - script->token; | | > > > > > > > > > > > > > > > > > > > > > > > > > | 8847 8848 8849 8850 8851 8852 8853 8854 8855 8856 8857 8858 8859 8860 8861 8862 8863 8864 8865 8866 8867 8868 8869 8870 8871 8872 8873 8874 8875 8876 8877 8878 8879 8880 8881 8882 8883 8884 8885 8886 8887 8888 8889 8890 8891 8892 8893 8894 8895 8896 | if (lineargs == 0) { token--; } script->len = token - script->token; JimPanic((script->len >= count, "allocated script array is too short")); #ifdef DEBUG_SHOW_SCRIPT printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj)); for (i = 0; i < script->len; i++) { const ScriptToken *t = &script->token[i]; printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr)); } #endif } static int JimParseCheckMissing(Jim_Interp *interp, int ch) { const char *msg; switch (ch) { case '\\': case ' ': return JIM_OK; case '[': msg = "unmatched \"[\""; break; case '{': msg = "missing close-brace"; break; case '"': default: msg = "missing quote"; break; } Jim_SetResultString(interp, msg, -1); return JIM_ERR; } static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, ParseTokenList *tokenlist) { int i; struct ScriptToken *token; |
︙ | ︙ | |||
8697 8698 8699 8700 8701 8702 8703 | Jim_IncrRefCount(token->objPtr); token++; } script->len = i; } | | | 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 | Jim_IncrRefCount(token->objPtr); token++; } script->len = i; } static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) { int scriptTextLen; const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); struct JimParserCtx parser; struct ScriptObj *script; ParseTokenList tokenlist; int line = 1; |
︙ | ︙ | |||
8720 8721 8722 8723 8724 8725 8726 | JimParserInit(&parser, scriptText, scriptTextLen, line); while (!parser.eof) { JimParseScript(&parser); ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, parser.tline); } | < < < < < < > > | < | > | | > | > > > > > > > > > > | 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 | JimParserInit(&parser, scriptText, scriptTextLen, line); while (!parser.eof) { JimParseScript(&parser); ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, parser.tline); } ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0); script = Jim_Alloc(sizeof(*script)); memset(script, 0, sizeof(*script)); script->inUse = 1; if (objPtr->typePtr == &sourceObjType) { script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; } else { script->fileNameObj = interp->emptyObj; } Jim_IncrRefCount(script->fileNameObj); script->missing = parser.missing.ch; script->linenr = parser.missing.line; ScriptObjAddTokens(interp, script, &tokenlist); ScriptTokenListFree(&tokenlist); Jim_FreeIntRep(interp, objPtr); Jim_SetIntRepPtr(objPtr, script); objPtr->typePtr = &scriptObjType; } static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script); ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr) { if (objPtr == interp->emptyObj) { objPtr = interp->nullScriptObj; } if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) { JimSetScriptFromAny(interp, objPtr); } return (ScriptObj *)Jim_GetIntRepPtr(objPtr); } static int JimScriptValid(Jim_Interp *interp, ScriptObj *script) { if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) { JimAddErrorToStack(interp, script); return 0; } return 1; } static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr) { cmdPtr->inUse++; } static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr) |
︙ | ︙ | |||
8848 8849 8850 8851 8852 8853 8854 8855 8856 8857 8858 8859 8860 8861 | else if (Jim_Length(interp->framePtr->nsObj)) { nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj); Jim_AppendStrings(interp, nsObj, "::", name, NULL); } return nsObj; } static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr) { Jim_Obj *objPtr = interp->emptyObj; if (name[0] == ':' && name[1] == ':') { | > > > > > > > > > > > > > > > > | 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 | else if (Jim_Length(interp->framePtr->nsObj)) { nsObj = Jim_DuplicateObj(interp, interp->framePtr->nsObj); Jim_AppendStrings(interp, nsObj, "::", name, NULL); } return nsObj; } Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr) { Jim_Obj *resultObj; const char *name = Jim_String(nameObjPtr); if (name[0] == ':' && name[1] == ':') { return nameObjPtr; } Jim_IncrRefCount(nameObjPtr); resultObj = Jim_NewStringObj(interp, "::", -1); Jim_AppendObj(interp, resultObj, nameObjPtr); Jim_DecrRefCount(interp, nameObjPtr); return resultObj; } static const char *JimQualifyName(Jim_Interp *interp, const char *name, Jim_Obj **objPtrPtr) { Jim_Obj *objPtr = interp->emptyObj; if (name[0] == ':' && name[1] == ':') { |
︙ | ︙ | |||
8875 8876 8877 8878 8879 8880 8881 8882 8883 8884 8885 8886 8887 8888 8889 8890 8891 8892 8893 | #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ)) #else #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME)) #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY) #endif static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd) { Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name); if (he) { Jim_InterpIncrProcEpoch(interp); } if (he && interp->local) { | > > > > > | | | 9106 9107 9108 9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135 9136 9137 9138 | #define JimFreeQualifiedName(INTERP, OBJ) Jim_DecrRefCount((INTERP), (OBJ)) #else #define JimQualifyName(INTERP, NAME, DUMMY) (((NAME)[0] == ':' && (NAME)[1] == ':') ? (NAME) + 2 : (NAME)) #define JimFreeQualifiedName(INTERP, DUMMY) (void)(DUMMY) Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr) { return nameObjPtr; } #endif static int JimCreateCommand(Jim_Interp *interp, const char *name, Jim_Cmd *cmd) { Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, name); if (he) { Jim_InterpIncrProcEpoch(interp); } if (he && interp->local) { cmd->prevCmd = Jim_GetHashEntryVal(he); Jim_SetHashVal(&interp->commands, he, cmd); } else { if (he) { Jim_DeleteHashEntry(&interp->commands, name); } |
︙ | ︙ | |||
8931 8932 8933 8934 8935 8936 8937 | if (len == 0) { return JIM_OK; } cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable)); Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp); for (i = 0; i < len; i++) { | | | | | | 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 | if (len == 0) { return JIM_OK; } cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable)); Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp); for (i = 0; i < len; i++) { Jim_Obj *objPtr, *initObjPtr, *nameObjPtr; Jim_Var *varPtr; int subLen; objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i); subLen = Jim_ListLength(interp, objPtr); if (subLen == 1 || subLen == 2) { nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0); if (subLen == 1) { initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE); if (initObjPtr == NULL) { Jim_SetResultFormatted(interp, "variable for initialization of static \"%#s\" not found in the local context", nameObjPtr); return JIM_ERR; } } else { initObjPtr = Jim_ListGetIndex(interp, objPtr, 1); } if (JimValidName(interp, "static variable", nameObjPtr) != JIM_OK) { return JIM_ERR; } varPtr = Jim_Alloc(sizeof(*varPtr)); varPtr->objPtr = initObjPtr; |
︙ | ︙ | |||
9036 9037 9038 9039 9040 9041 9042 | for (i = 0; i < argListLen; i++) { Jim_Obj *argPtr; Jim_Obj *nameObjPtr; Jim_Obj *defaultObjPtr; int len; | | | | | 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301 9302 | for (i = 0; i < argListLen; i++) { Jim_Obj *argPtr; Jim_Obj *nameObjPtr; Jim_Obj *defaultObjPtr; int len; argPtr = Jim_ListGetIndex(interp, argListObjPtr, i); len = Jim_ListLength(interp, argPtr); if (len == 0) { Jim_SetResultString(interp, "argument with no name", -1); err: JimDecrCmdRefCount(interp, cmdPtr); return NULL; } if (len > 2) { Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr); goto err; } if (len == 2) { nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0); defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1); } else { nameObjPtr = argPtr; defaultObjPtr = NULL; } |
︙ | ︙ | |||
9130 9131 9132 9133 9134 9135 9136 | Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName); } else if (Jim_FindHashEntry(&interp->commands, fqnew)) { Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName); } else { | | | 9366 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 | Jim_SetResultFormatted(interp, "can't rename \"%s\": command doesn't exist", oldName); } else if (Jim_FindHashEntry(&interp->commands, fqnew)) { Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName); } else { cmdPtr = Jim_GetHashEntryVal(he); JimIncrCmdRefCount(cmdPtr); JimUpdateProcNamespace(interp, cmdPtr, fqnew); Jim_AddHashEntry(&interp->commands, fqnew, cmdPtr); Jim_DeleteHashEntry(&interp->commands, fqold); |
︙ | ︙ | |||
9215 9216 9217 9218 9219 9220 9221 | Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr); } return NULL; } #ifdef jim_ext_namespace found: #endif | | | 9451 9452 9453 9454 9455 9456 9457 9458 9459 9460 9461 9462 9463 9464 9465 | Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr); } return NULL; } #ifdef jim_ext_namespace found: #endif cmd = Jim_GetHashEntryVal(he); Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &commandObjType; objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch; objPtr->internalRep.cmdValue.cmdPtr = cmd; objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj; |
︙ | ︙ | |||
9321 9322 9323 9324 9325 9326 9327 | } } Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &variableObjType; objPtr->internalRep.varValue.callFrameId = framePtr->id; | | | 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 9568 9569 9570 9571 | } } Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &variableObjType; objPtr->internalRep.varValue.callFrameId = framePtr->id; objPtr->internalRep.varValue.varPtr = Jim_GetHashEntryVal(he); objPtr->internalRep.varValue.global = global; return JIM_OK; } static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr); static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags); |
︙ | ︙ | |||
9639 9640 9641 9642 9643 9644 9645 | else { framePtr = interp->framePtr; } retval = Jim_DeleteHashEntry(&framePtr->vars, name); if (retval == JIM_OK) { | | | 9875 9876 9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 | else { framePtr = interp->framePtr; } retval = Jim_DeleteHashEntry(&framePtr->vars, name); if (retval == JIM_OK) { framePtr->id = interp->callFrameEpoch++; } } } if (retval != JIM_OK && (flags & JIM_ERRMSG)) { Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr); } return retval; |
︙ | ︙ | |||
9723 9724 9725 9726 9727 9728 9729 | dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG); if (!dictObjPtr) { return NULL; } ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE); if (ret != JIM_OK) { | < < | | < < < | | < < < | < < | < | 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 | dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG); if (!dictObjPtr) { return NULL; } ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE); if (ret != JIM_OK) { Jim_SetResultFormatted(interp, "can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr, ret < 0 ? "variable isn't" : "no such element in"); } else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) { Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr)); } return resObjPtr; } static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags) |
︙ | ︙ | |||
9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 | static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj) { Jim_CallFrame *cf; if (interp->freeFramesList) { cf = interp->freeFramesList; interp->freeFramesList = cf->next; } else { cf = Jim_Alloc(sizeof(*cf)); | > > > > > > > > > > > > | > < < < < < < < < | < < < < < < < > | | | | | | | | > > | | | | < < < < | 10066 10067 10068 10069 10070 10071 10072 10073 10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085 10086 10087 10088 10089 10090 10091 10092 10093 10094 10095 10096 10097 10098 10099 10100 10101 10102 10103 10104 10105 10106 10107 10108 10109 10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 10120 10121 10122 10123 10124 10125 10126 10127 10128 10129 10130 10131 10132 10133 10134 10135 10136 10137 10138 10139 10140 10141 10142 10143 10144 10145 10146 10147 10148 10149 10150 10151 10152 10153 10154 10155 10156 10157 10158 10159 10160 10161 10162 10163 10164 10165 10166 10167 10168 10169 10170 10171 10172 10173 10174 10175 10176 10177 10178 10179 10180 10181 10182 10183 10184 | static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj) { Jim_CallFrame *cf; if (interp->freeFramesList) { cf = interp->freeFramesList; interp->freeFramesList = cf->next; cf->argv = NULL; cf->argc = 0; cf->procArgsObjPtr = NULL; cf->procBodyObjPtr = NULL; cf->next = NULL; cf->staticVars = NULL; cf->localCommands = NULL; cf->tailcall = 0; cf->tailcallObj = NULL; cf->tailcallCmd = NULL; } else { cf = Jim_Alloc(sizeof(*cf)); memset(cf, 0, sizeof(*cf)); Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp); } cf->id = interp->callFrameEpoch++; cf->parent = parent; cf->level = parent ? parent->level + 1 : 0; cf->nsObj = nsObj; Jim_IncrRefCount(nsObj); return cf; } static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands) { if (localCommands) { Jim_Obj *cmdNameObj; while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) { Jim_HashEntry *he; Jim_Obj *fqObjName; Jim_HashTable *ht = &interp->commands; const char *fqname = JimQualifyName(interp, Jim_String(cmdNameObj), &fqObjName); he = Jim_FindHashEntry(ht, fqname); if (he) { Jim_Cmd *cmd = Jim_GetHashEntryVal(he); if (cmd->prevCmd) { Jim_Cmd *prevCmd = cmd->prevCmd; cmd->prevCmd = NULL; JimDecrCmdRefCount(interp, cmd); Jim_SetHashVal(ht, he, prevCmd); } else { Jim_DeleteHashEntry(ht, fqname); Jim_InterpIncrProcEpoch(interp); } } Jim_DecrRefCount(interp, cmdNameObj); JimFreeQualifiedName(interp, fqObjName); } Jim_FreeStack(localCommands); Jim_Free(localCommands); } return JIM_OK; } #define JIM_FCF_FULL 0 #define JIM_FCF_REUSE 1 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action) { JimDeleteLocalProcs(interp, cf->localCommands); if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr); if (cf->procBodyObjPtr) Jim_DecrRefCount(interp, cf->procBodyObjPtr); Jim_DecrRefCount(interp, cf->nsObj); if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE) Jim_FreeHashTable(&cf->vars); else { int i; Jim_HashEntry **table = cf->vars.table, *he; for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) { he = table[i]; while (he != NULL) { Jim_HashEntry *nextEntry = he->next; Jim_Var *varPtr = Jim_GetHashEntryVal(he); Jim_DecrRefCount(interp, varPtr->objPtr); Jim_Free(Jim_GetHashEntryKey(he)); Jim_Free(varPtr); Jim_Free(he); table[i] = NULL; he = nextEntry; } } cf->vars.used = 0; } cf->next = interp->freeFramesList; interp->freeFramesList = cf; } #ifdef JIM_REFERENCES static void JimReferencesHTValDestructor(void *interp, void *val) { |
︙ | ︙ | |||
10029 10030 10031 10032 10033 10034 10035 | "reference", NULL, NULL, UpdateStringOfReference, JIM_TYPE_REFERENCES, }; | | < < | < < < | | 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 10266 10267 10268 10269 10270 | "reference", NULL, NULL, UpdateStringOfReference, JIM_TYPE_REFERENCES, }; static void UpdateStringOfReference(struct Jim_Obj *objPtr) { char buf[JIM_REFERENCE_SPACE + 1]; JimFormatReference(buf, objPtr->internalRep.refValue.refPtr, objPtr->internalRep.refValue.id); JimSetStringBytes(objPtr, buf); } static int isrefchar(int c) { return (c == '_' || isalnum(c)); } |
︙ | ︙ | |||
10094 10095 10096 10097 10098 10099 10100 | goto badformat; he = Jim_FindHashEntry(&interp->references, &value); if (he == NULL) { Jim_SetResultFormatted(interp, "invalid reference id \"%#s\"", objPtr); return JIM_ERR; } | | | 10311 10312 10313 10314 10315 10316 10317 10318 10319 10320 10321 10322 10323 10324 10325 | goto badformat; he = Jim_FindHashEntry(&interp->references, &value); if (he == NULL) { Jim_SetResultFormatted(interp, "invalid reference id \"%#s\"", objPtr); return JIM_ERR; } refPtr = Jim_GetHashEntryVal(he); Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &referenceObjType; objPtr->internalRep.refValue.id = value; objPtr->internalRep.refValue.refPtr = refPtr; return JIM_OK; |
︙ | ︙ | |||
10211 10212 10213 10214 10215 10216 10217 | if (elapsedId > JIM_COLLECT_ID_PERIOD || elapsedTime > JIM_COLLECT_TIME_PERIOD) { Jim_Collect(interp); } } #endif | | | 10428 10429 10430 10431 10432 10433 10434 10435 10436 10437 10438 10439 10440 10441 10442 | if (elapsedId > JIM_COLLECT_ID_PERIOD || elapsedTime > JIM_COLLECT_TIME_PERIOD) { Jim_Collect(interp); } } #endif int Jim_IsBigEndian(void) { union { unsigned short s; unsigned char c[2]; } uval = {0x0102}; return uval.c[0] == 1; |
︙ | ︙ | |||
10267 10268 10269 10270 10271 10272 10273 | Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY); Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0"); Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS); Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM); Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR); | | | > > > > > > > | < < < < < | | | | | | > > | > | < | | | < < < < | 10484 10485 10486 10487 10488 10489 10490 10491 10492 10493 10494 10495 10496 10497 10498 10499 10500 10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 10514 10515 10516 10517 10518 10519 10520 10521 10522 10523 10524 10525 10526 10527 10528 10529 10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 | Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY); Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0"); Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS); Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM); Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR); Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian"); Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0"); Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *))); Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide))); return i; } void Jim_FreeInterp(Jim_Interp *i) { Jim_CallFrame *cf, *cfx; Jim_Obj *objPtr, *nextObjPtr; for (cf = i->framePtr; cf; cf = cfx) { cfx = cf->parent; JimFreeCallFrame(i, cf, JIM_FCF_FULL); } Jim_DecrRefCount(i, i->emptyObj); Jim_DecrRefCount(i, i->trueObj); Jim_DecrRefCount(i, i->falseObj); Jim_DecrRefCount(i, i->result); Jim_DecrRefCount(i, i->stackTrace); Jim_DecrRefCount(i, i->errorProc); Jim_DecrRefCount(i, i->unknown); Jim_DecrRefCount(i, i->errorFileNameObj); Jim_DecrRefCount(i, i->currentScriptObj); Jim_DecrRefCount(i, i->nullScriptObj); Jim_FreeHashTable(&i->commands); #ifdef JIM_REFERENCES Jim_FreeHashTable(&i->references); #endif Jim_FreeHashTable(&i->packages); Jim_Free(i->prngState); Jim_FreeHashTable(&i->assocData); #ifdef JIM_MAINTAINER if (i->liveList != NULL) { objPtr = i->liveList; printf("\n-------------------------------------\n"); printf("Objects still in the free list:\n"); while (objPtr) { const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string"; if (objPtr->bytes && strlen(objPtr->bytes) > 20) { printf("%p (%d) %-10s: '%.20s...'\n", (void *)objPtr, objPtr->refCount, type, objPtr->bytes); } else { printf("%p (%d) %-10s: '%s'\n", (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)"); } if (objPtr->typePtr == &sourceObjType) { printf("FILE %s LINE %d\n", Jim_String(objPtr->internalRep.sourceValue.fileNameObj), objPtr->internalRep.sourceValue.lineNumber); } objPtr = objPtr->nextObjPtr; } printf("-------------------------------------\n\n"); JimPanic((1, "Live list non empty freeing the interpreter! Leak?")); } #endif objPtr = i->freeList; while (objPtr) { nextObjPtr = objPtr->nextObjPtr; Jim_Free(objPtr); objPtr = nextObjPtr; } for (cf = i->freeFramesList; cf; cf = cfx) { cfx = cf->next; if (cf->vars.table) Jim_FreeHashTable(&cf->vars); Jim_Free(cf); } Jim_Free(i); } Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr) { |
︙ | ︙ | |||
10447 10448 10449 10450 10451 10452 10453 | Jim_IncrRefCount(stackTraceObj); Jim_DecrRefCount(interp, interp->stackTrace); interp->stackTrace = stackTraceObj; interp->errorFlag = 1; len = Jim_ListLength(interp, interp->stackTrace); if (len >= 3) { | < < | < < < < < | 10664 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675 10676 10677 10678 10679 10680 10681 10682 | Jim_IncrRefCount(stackTraceObj); Jim_DecrRefCount(interp, interp->stackTrace); interp->stackTrace = stackTraceObj; interp->errorFlag = 1; len = Jim_ListLength(interp, interp->stackTrace); if (len >= 3) { if (Jim_Length(Jim_ListGetIndex(interp, interp->stackTrace, len - 2)) == 0) { interp->addStackTrace = 1; } } } static void JimAppendStackTrace(Jim_Interp *interp, const char *procname, Jim_Obj *fileNameObj, int linenr) { if (strcmp(procname, "unknown") == 0) { procname = ""; } |
︙ | ︙ | |||
10483 10484 10485 10486 10487 10488 10489 | if (!*procname && Jim_Length(fileNameObj)) { int len = Jim_ListLength(interp, interp->stackTrace); if (len >= 3) { | | | | > | 10693 10694 10695 10696 10697 10698 10699 10700 10701 10702 10703 10704 10705 10706 10707 10708 10709 10710 10711 | if (!*procname && Jim_Length(fileNameObj)) { int len = Jim_ListLength(interp, interp->stackTrace); if (len >= 3) { Jim_Obj *objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 3); if (Jim_Length(objPtr)) { objPtr = Jim_ListGetIndex(interp, interp->stackTrace, len - 2); if (Jim_Length(objPtr) == 0) { ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0); ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0); return; } } } |
︙ | ︙ | |||
10516 10517 10518 10519 10520 10521 10522 | } void *Jim_GetAssocData(Jim_Interp *interp, const char *key) { Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key); if (entryPtr != NULL) { | | < | 10727 10728 10729 10730 10731 10732 10733 10734 10735 10736 10737 10738 10739 10740 10741 | } void *Jim_GetAssocData(Jim_Interp *interp, const char *key) { Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key); if (entryPtr != NULL) { AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr); return assocEntryPtr->data; } return NULL; } int Jim_DeleteAssocData(Jim_Interp *interp, const char *key) { |
︙ | ︙ | |||
10555 10556 10557 10558 10559 10560 10561 | UpdateStringOfInt, JIM_TYPE_NONE, }; static void UpdateStringOfInt(struct Jim_Obj *objPtr) { | < > > > > > > > > > | > > > > > > | > > > > | > > > > > | > | | 10765 10766 10767 10768 10769 10770 10771 10772 10773 10774 10775 10776 10777 10778 10779 10780 10781 10782 10783 10784 10785 10786 10787 10788 10789 10790 10791 10792 10793 10794 10795 10796 10797 10798 10799 10800 10801 10802 10803 10804 10805 10806 10807 10808 10809 10810 10811 10812 | UpdateStringOfInt, JIM_TYPE_NONE, }; static void UpdateStringOfInt(struct Jim_Obj *objPtr) { char buf[JIM_INTEGER_SPACE + 1]; jim_wide wideValue = JimWideValue(objPtr); int pos = 0; if (wideValue == 0) { buf[pos++] = '0'; } else { char tmp[JIM_INTEGER_SPACE]; int num = 0; int i; if (wideValue < 0) { buf[pos++] = '-'; i = wideValue % 10; tmp[num++] = (i > 0) ? (10 - i) : -i; wideValue /= -10; } while (wideValue) { tmp[num++] = wideValue % 10; wideValue /= 10; } for (i = 0; i < num; i++) { buf[pos++] = '0' + tmp[num - i - 1]; } } buf[pos] = 0; JimSetStringBytes(objPtr, buf); } static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags) { jim_wide wideValue; const char *str; if (objPtr->typePtr == &coercedDoubleObjType) { objPtr->typePtr = &intObjType; |
︙ | ︙ | |||
10656 10657 10658 10659 10660 10661 10662 | "double", NULL, NULL, UpdateStringOfDouble, JIM_TYPE_NONE, }; | > > > > > > > > > | > | > > > > > > > > > > > > > > | > > | > > > > > | > > > > > > > > | > > > | | | > | | 10890 10891 10892 10893 10894 10895 10896 10897 10898 10899 10900 10901 10902 10903 10904 10905 10906 10907 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 10950 10951 10952 10953 10954 10955 10956 10957 10958 | "double", NULL, NULL, UpdateStringOfDouble, JIM_TYPE_NONE, }; #ifndef HAVE_ISNAN #undef isnan #define isnan(X) ((X) != (X)) #endif #ifndef HAVE_ISINF #undef isinf #define isinf(X) (1.0 / (X) == 0.0) #endif static void UpdateStringOfDouble(struct Jim_Obj *objPtr) { double value = objPtr->internalRep.doubleValue; if (isnan(value)) { JimSetStringBytes(objPtr, "NaN"); return; } if (isinf(value)) { if (value < 0) { JimSetStringBytes(objPtr, "-Inf"); } else { JimSetStringBytes(objPtr, "Inf"); } return; } { char buf[JIM_DOUBLE_SPACE + 1]; int i; int len = sprintf(buf, "%.12g", value); for (i = 0; i < len; i++) { if (buf[i] == '.' || buf[i] == 'e') { #if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX) char *e = strchr(buf, 'e'); if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') { e += 2; memmove(e, e + 1, len - (e - buf)); } #endif break; } } if (buf[i] == '\0') { buf[i++] = '.'; buf[i++] = '0'; buf[i] = '\0'; } JimSetStringBytes(objPtr, buf); } } static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { double doubleValue; jim_wide wideValue; const char *str; str = Jim_String(objPtr); |
︙ | ︙ | |||
10700 10701 10702 10703 10704 10705 10706 | objPtr->typePtr = &coercedDoubleObjType; objPtr->internalRep.wideValue = wideValue; return JIM_OK; } else { if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) { | | | 10977 10978 10979 10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 10991 | objPtr->typePtr = &coercedDoubleObjType; objPtr->internalRep.wideValue = wideValue; return JIM_OK; } else { if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) { Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr); return JIM_ERR; } Jim_FreeIntRep(interp, objPtr); } objPtr->typePtr = &doubleObjType; objPtr->internalRep.doubleValue = doubleValue; |
︙ | ︙ | |||
10879 10880 10881 10882 10883 10884 10885 | } } return JIM_ELESTR_SIMPLE; } return JIM_ELESTR_QUOTE; } | | | | 11156 11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169 11170 11171 11172 11173 11174 | } } return JIM_ELESTR_SIMPLE; } return JIM_ELESTR_QUOTE; } static int BackslashQuoteString(const char *s, int len, char *q) { char *p = q; while (len--) { switch (*s) { case ' ': case '$': case '"': case '[': case ']': case '{': |
︙ | ︙ | |||
10999 11000 11001 11002 11003 11004 11005 | realLength += len + 2; break; case JIM_ELESTR_QUOTE: if (i == 0 && strRep[0] == '#') { *p++ = '\\'; realLength++; } | | | 11276 11277 11278 11279 11280 11281 11282 11283 11284 11285 11286 11287 11288 11289 11290 | realLength += len + 2; break; case JIM_ELESTR_QUOTE: if (i == 0 && strRep[0] == '#') { *p++ = '\\'; realLength++; } qlen = BackslashQuoteString(strRep, len, p); p += qlen; realLength += qlen; break; } if (i + 1 != objc) { *p++ = ' '; |
︙ | ︙ | |||
11035 11036 11037 11038 11039 11040 11041 | Jim_Obj *fileNameObj; int linenr; if (objPtr->typePtr == &listObjType) { return JIM_OK; } | | | 11312 11313 11314 11315 11316 11317 11318 11319 11320 11321 11322 11323 11324 11325 11326 | Jim_Obj *fileNameObj; int linenr; if (objPtr->typePtr == &listObjType) { return JIM_OK; } if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) { Jim_Obj **listObjPtrPtr; int len; int i; listObjPtrPtr = JimDictPairs(objPtr, &len); for (i = 0; i < len; i++) { Jim_IncrRefCount(listObjPtrPtr[i]); |
︙ | ︙ | |||
11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 | jmp_buf jmpbuf; Jim_Obj *command; Jim_Interp *interp; enum { JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_INTEGER, JIM_LSORT_COMMAND } type; int order; int index; int indexed; int (*subfn)(Jim_Obj **, Jim_Obj **); }; static struct lsort_info *sort_info; static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { | > > | 11416 11417 11418 11419 11420 11421 11422 11423 11424 11425 11426 11427 11428 11429 11430 11431 11432 11433 11434 11435 11436 | jmp_buf jmpbuf; Jim_Obj *command; Jim_Interp *interp; enum { JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_INTEGER, JIM_LSORT_REAL, JIM_LSORT_COMMAND } type; int order; int index; int indexed; int unique; int (*subfn)(Jim_Obj **, Jim_Obj **); }; static struct lsort_info *sort_info; static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { |
︙ | ︙ | |||
11182 11183 11184 11185 11186 11187 11188 11189 11190 11191 11192 11193 11194 11195 | if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK || Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { longjmp(sort_info->jmpbuf, JIM_ERR); } return JimSign(lhs - rhs) * sort_info->order; } static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { Jim_Obj *compare_script; int rc; jim_wide ret = 0; | > > > > > > > > > > > > > > > > > | 11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 11486 11487 11488 11489 11490 11491 | if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK || Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { longjmp(sort_info->jmpbuf, JIM_ERR); } return JimSign(lhs - rhs) * sort_info->order; } static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { double lhs = 0, rhs = 0; if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK || Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { longjmp(sort_info->jmpbuf, JIM_ERR); } if (lhs == rhs) { return 0; } if (lhs > rhs) { return sort_info->order; } return -sort_info->order; } static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { Jim_Obj *compare_script; int rc; jim_wide ret = 0; |
︙ | ︙ | |||
11204 11205 11206 11207 11208 11209 11210 11211 11212 11213 11214 11215 11216 11217 11218 11219 11220 11221 | if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) { longjmp(sort_info->jmpbuf, rc); } return JimSign(ret) * sort_info->order; } static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info) { struct lsort_info *prev_info; typedef int (qsort_comparator) (const void *, const void *); int (*fn) (Jim_Obj **, Jim_Obj **); Jim_Obj **vector; int len; int rc; | > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > | > | 11500 11501 11502 11503 11504 11505 11506 11507 11508 11509 11510 11511 11512 11513 11514 11515 11516 11517 11518 11519 11520 11521 11522 11523 11524 11525 11526 11527 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 11545 11546 11547 11548 11549 11550 11551 11552 11553 11554 11555 11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 11578 11579 11580 11581 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 | if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) { longjmp(sort_info->jmpbuf, rc); } return JimSign(ret) * sort_info->order; } static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs)) { int src; int dst = 0; Jim_Obj **ele = listObjPtr->internalRep.listValue.ele; for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) { if (comp(&ele[dst], &ele[src]) == 0) { Jim_DecrRefCount(sort_info->interp, ele[dst]); } else { dst++; } ele[dst] = ele[src]; } ele[++dst] = ele[src]; listObjPtr->internalRep.listValue.len = dst; } static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info) { struct lsort_info *prev_info; typedef int (qsort_comparator) (const void *, const void *); int (*fn) (Jim_Obj **, Jim_Obj **); Jim_Obj **vector; int len; int rc; JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object")); SetListFromAny(interp, listObjPtr); prev_info = sort_info; sort_info = info; vector = listObjPtr->internalRep.listValue.ele; len = listObjPtr->internalRep.listValue.len; switch (info->type) { case JIM_LSORT_ASCII: fn = ListSortString; break; case JIM_LSORT_NOCASE: fn = ListSortStringNoCase; break; case JIM_LSORT_INTEGER: fn = ListSortInteger; break; case JIM_LSORT_REAL: fn = ListSortReal; break; case JIM_LSORT_COMMAND: fn = ListSortCommand; break; default: fn = NULL; JimPanic((1, "ListSort called with invalid sort type")); } if (info->indexed) { info->subfn = fn; fn = ListSortIndexHelper; } if ((rc = setjmp(info->jmpbuf)) == 0) { qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn); if (info->unique && len > 1) { ListRemoveDuplicates(listObjPtr, fn); } Jim_InvalidateStringRep(listObjPtr); } sort_info = prev_info; return rc; } static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec) { |
︙ | ︙ | |||
11380 11381 11382 11383 11384 11385 11386 | idx = listPtr->internalRep.listValue.len + idx; Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]); listPtr->internalRep.listValue.ele[idx] = newObjPtr; Jim_IncrRefCount(newObjPtr); return JIM_OK; } | | | 11708 11709 11710 11711 11712 11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 | idx = listPtr->internalRep.listValue.len + idx; Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]); listPtr->internalRep.listValue.ele[idx] = newObjPtr; Jim_IncrRefCount(newObjPtr); return JIM_OK; } int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr) { Jim_Obj *varObjPtr, *objPtr, *listObjPtr; int shared, i, idx; varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED); if (objPtr == NULL) |
︙ | ︙ | |||
11428 11429 11430 11431 11432 11433 11434 | Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen) { int i; int listLen = Jim_ListLength(interp, listObjPtr); Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp); for (i = 0; i < listLen; ) { | < < < | | 11756 11757 11758 11759 11760 11761 11762 11763 11764 11765 11766 11767 11768 11769 11770 | Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen) { int i; int listLen = Jim_ListLength(interp, listObjPtr); Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp); for (i = 0; i < listLen; ) { Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i)); if (++i != listLen) { Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen); } } return resObjPtr; } |
︙ | ︙ | |||
11461 11462 11463 11464 11465 11466 11467 | else { int len = 0, objLen; char *bytes, *p; for (i = 0; i < objc; i++) { | < | | | < > | | < | | > | 11786 11787 11788 11789 11790 11791 11792 11793 11794 11795 11796 11797 11798 11799 11800 11801 11802 11803 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 11816 11817 11818 11819 11820 11821 11822 11823 11824 11825 11826 11827 11828 11829 11830 11831 | else { int len = 0, objLen; char *bytes, *p; for (i = 0; i < objc; i++) { len += Jim_Length(objv[i]); } if (objc) len += objc - 1; p = bytes = Jim_Alloc(len + 1); for (i = 0; i < objc; i++) { const char *s = Jim_GetString(objv[i], &objLen); while (objLen && isspace(UCHAR(*s))) { s++; objLen--; len--; } while (objLen && isspace(UCHAR(s[objLen - 1]))) { if (objLen > 1 && s[objLen - 2] == '\\') { break; } objLen--; len--; } memcpy(p, s, objLen); p += objLen; if (i + 1 != objc) { if (objLen) *p++ = ' '; else { len--; } } } *p = '\0'; return Jim_NewStringObjNoAlloc(interp, bytes, len); } } |
︙ | ︙ | |||
11537 11538 11539 11540 11541 11542 11543 11544 11545 11546 11547 11548 11549 11550 11551 | return Jim_GenHashFunction((const unsigned char *)str, len); } static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2) { return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2); } static void JimObjectHTKeyValDestructor(void *interp, void *val) { Jim_DecrRefCount(interp, (Jim_Obj *)val); } static const Jim_HashTableType JimDictHashTableType = { JimObjectHTHashFunction, | > > > > > > < < > > | 11861 11862 11863 11864 11865 11866 11867 11868 11869 11870 11871 11872 11873 11874 11875 11876 11877 11878 11879 11880 11881 11882 11883 11884 11885 11886 11887 11888 11889 11890 | return Jim_GenHashFunction((const unsigned char *)str, len); } static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2) { return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2); } static void *JimObjectHTKeyValDup(void *privdata, const void *val) { Jim_IncrRefCount((Jim_Obj *)val); return (void *)val; } static void JimObjectHTKeyValDestructor(void *interp, void *val) { Jim_DecrRefCount(interp, (Jim_Obj *)val); } static const Jim_HashTableType JimDictHashTableType = { JimObjectHTHashFunction, JimObjectHTKeyValDup, JimObjectHTKeyValDup, JimObjectHTKeyCompare, JimObjectHTKeyValDestructor, JimObjectHTKeyValDestructor }; static const Jim_ObjType dictObjType = { "dict", |
︙ | ︙ | |||
11583 11584 11585 11586 11587 11588 11589 | dupHt = Jim_Alloc(sizeof(*dupHt)); Jim_InitHashTable(dupHt, &JimDictHashTableType, interp); if (ht->size != 0) Jim_ExpandHashTable(dupHt, ht->size); JimInitHashTableIterator(ht, &htiter); while ((he = Jim_NextHashEntry(&htiter)) != NULL) { | < < < < < | | 11913 11914 11915 11916 11917 11918 11919 11920 11921 11922 11923 11924 11925 11926 11927 | dupHt = Jim_Alloc(sizeof(*dupHt)); Jim_InitHashTable(dupHt, &JimDictHashTableType, interp); if (ht->size != 0) Jim_ExpandHashTable(dupHt, ht->size); JimInitHashTableIterator(ht, &htiter); while ((he = Jim_NextHashEntry(&htiter)) != NULL) { Jim_AddHashEntry(dupHt, he->key, he->u.val); } dupPtr->internalRep.ptr = dupHt; dupPtr->typePtr = &dictObjType; } static Jim_Obj **JimDictPairs(Jim_Obj *dictPtr, int *len) |
︙ | ︙ | |||
11610 11611 11612 11613 11614 11615 11616 | ht = dictPtr->internalRep.ptr; objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *)); JimInitHashTableIterator(ht, &htiter); i = 0; while ((he = Jim_NextHashEntry(&htiter)) != NULL) { | | | > > | > | | < < < < < < | < < < < < < < < < < | < < < < < < | | 11935 11936 11937 11938 11939 11940 11941 11942 11943 11944 11945 11946 11947 11948 11949 11950 11951 11952 11953 11954 11955 11956 11957 11958 11959 11960 11961 11962 11963 11964 11965 11966 11967 11968 11969 11970 11971 11972 11973 11974 11975 11976 11977 11978 11979 11980 11981 11982 11983 11984 11985 11986 11987 11988 11989 11990 11991 11992 11993 11994 11995 11996 11997 11998 11999 12000 12001 12002 12003 12004 12005 12006 12007 12008 12009 12010 12011 12012 12013 12014 12015 12016 12017 12018 12019 12020 12021 12022 12023 12024 12025 12026 12027 12028 12029 12030 12031 | ht = dictPtr->internalRep.ptr; objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *)); JimInitHashTableIterator(ht, &htiter); i = 0; while ((he = Jim_NextHashEntry(&htiter)) != NULL) { objv[i++] = Jim_GetHashEntryKey(he); objv[i++] = Jim_GetHashEntryVal(he); } *len = i; return objv; } static void UpdateStringOfDict(struct Jim_Obj *objPtr) { int len; Jim_Obj **objv = JimDictPairs(objPtr, &len); JimMakeListStringRep(objPtr, objv, len); Jim_Free(objv); } static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) { int listlen; if (objPtr->typePtr == &dictObjType) { return JIM_OK; } if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) { Jim_String(objPtr); } listlen = Jim_ListLength(interp, objPtr); if (listlen % 2) { Jim_SetResultString(interp, "missing value to go with key", -1); return JIM_ERR; } else { Jim_HashTable *ht; int i; ht = Jim_Alloc(sizeof(*ht)); Jim_InitHashTable(ht, &JimDictHashTableType, interp); for (i = 0; i < listlen; i += 2) { Jim_Obj *keyObjPtr = Jim_ListGetIndex(interp, objPtr, i); Jim_Obj *valObjPtr = Jim_ListGetIndex(interp, objPtr, i + 1); Jim_ReplaceHashEntry(ht, keyObjPtr, valObjPtr); } Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &dictObjType; objPtr->internalRep.ptr = ht; return JIM_OK; } } static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) { Jim_HashTable *ht = objPtr->internalRep.ptr; if (valueObjPtr == NULL) { return Jim_DeleteHashEntry(ht, keyObjPtr); } Jim_ReplaceHashEntry(ht, keyObjPtr, valueObjPtr); return JIM_OK; } int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) { JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object")); if (SetDictFromAny(interp, objPtr) != JIM_OK) { return JIM_ERR; } Jim_InvalidateStringRep(objPtr); return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr); } Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len) { Jim_Obj *objPtr; int i; |
︙ | ︙ | |||
11842 11843 11844 11845 11846 11847 11848 11849 11850 11851 11852 11853 11854 11855 | if (newObjPtr == NULL) { goto err; } objPtr = Jim_NewDictObj(interp, NULL, 0); DictAddElement(interp, dictObjPtr, keyv[i], objPtr); } } Jim_InvalidateStringRep(objPtr); Jim_InvalidateStringRep(varObjPtr); if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) { goto err; } Jim_SetResult(interp, varObjPtr); return JIM_OK; | > | 12148 12149 12150 12151 12152 12153 12154 12155 12156 12157 12158 12159 12160 12161 12162 | if (newObjPtr == NULL) { goto err; } objPtr = Jim_NewDictObj(interp, NULL, 0); DictAddElement(interp, dictObjPtr, keyv[i], objPtr); } } Jim_InvalidateStringRep(objPtr); Jim_InvalidateStringRep(varObjPtr); if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) { goto err; } Jim_SetResult(interp, varObjPtr); return JIM_OK; |
︙ | ︙ | |||
11867 11868 11869 11870 11871 11872 11873 | "index", NULL, NULL, UpdateStringOfIndex, JIM_TYPE_NONE, }; | | | | | > > | | < < > | > | | < < | | | > | | 12174 12175 12176 12177 12178 12179 12180 12181 12182 12183 12184 12185 12186 12187 12188 12189 12190 12191 12192 12193 12194 12195 12196 12197 12198 12199 12200 12201 12202 12203 12204 12205 12206 | "index", NULL, NULL, UpdateStringOfIndex, JIM_TYPE_NONE, }; static void UpdateStringOfIndex(struct Jim_Obj *objPtr) { if (objPtr->internalRep.intValue == -1) { JimSetStringBytes(objPtr, "end"); } else { char buf[JIM_INTEGER_SPACE + 1]; if (objPtr->internalRep.intValue >= 0) { sprintf(buf, "%d", objPtr->internalRep.intValue); } else { sprintf(buf, "end%d", objPtr->internalRep.intValue + 1); } JimSetStringBytes(objPtr, buf); } } static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { int idx, end = 0; const char *str; char *endptr; str = Jim_String(objPtr); |
︙ | ︙ | |||
11956 11957 11958 11959 11960 11961 11962 | int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr) { if (objPtr->typePtr == &intObjType) { jim_wide val = JimWideValue(objPtr); | | > > > > | | < | 12264 12265 12266 12267 12268 12269 12270 12271 12272 12273 12274 12275 12276 12277 12278 12279 12280 12281 12282 12283 12284 | int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr) { if (objPtr->typePtr == &intObjType) { jim_wide val = JimWideValue(objPtr); if (val < 0) *indexPtr = -INT_MAX; else if (val > INT_MAX) *indexPtr = INT_MAX; else *indexPtr = (int)val; return JIM_OK; } if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR) return JIM_ERR; *indexPtr = objPtr->internalRep.intValue; return JIM_OK; } |
︙ | ︙ | |||
11983 11984 11985 11986 11987 11988 11989 | "exit", "eval", NULL }; #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes)) | < < | | 12294 12295 12296 12297 12298 12299 12300 12301 12302 12303 12304 12305 12306 12307 12308 12309 12310 12311 12312 12313 12314 12315 12316 12317 12318 12319 12320 12321 12322 12323 12324 12325 12326 | "exit", "eval", NULL }; #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes)) static const Jim_ObjType returnCodeObjType = { "return-code", NULL, NULL, NULL, JIM_TYPE_NONE, }; const char *Jim_ReturnCode(int code) { if (code < 0 || code >= (int)jimReturnCodesSize) { return "?"; } else { return jimReturnCodes[code]; } } static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { int returnCode; jim_wide wideValue; if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR) returnCode = (int)wideValue; |
︙ | ︙ | |||
12098 12099 12100 12101 12102 12103 12104 12105 12106 12107 12108 12109 12110 12111 | JIM_EXPROP_BITNOT, JIM_EXPROP_UNARYMINUS, JIM_EXPROP_UNARYPLUS, JIM_EXPROP_FUNC_FIRST, JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST, JIM_EXPROP_FUNC_ABS, JIM_EXPROP_FUNC_DOUBLE, JIM_EXPROP_FUNC_ROUND, JIM_EXPROP_FUNC_RAND, JIM_EXPROP_FUNC_SRAND, | > | 12407 12408 12409 12410 12411 12412 12413 12414 12415 12416 12417 12418 12419 12420 12421 | JIM_EXPROP_BITNOT, JIM_EXPROP_UNARYMINUS, JIM_EXPROP_UNARYPLUS, JIM_EXPROP_FUNC_FIRST, JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST, JIM_EXPROP_FUNC_WIDE, JIM_EXPROP_FUNC_ABS, JIM_EXPROP_FUNC_DOUBLE, JIM_EXPROP_FUNC_ROUND, JIM_EXPROP_FUNC_RAND, JIM_EXPROP_FUNC_SRAND, |
︙ | ︙ | |||
12155 12156 12157 12158 12159 12160 12161 | static Jim_Obj *ExprPop(struct JimExprState *e) { return e->stack[--e->stacklen]; } static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e) { | | < < < < > > < < < > < < > > > < < | < | 12465 12466 12467 12468 12469 12470 12471 12472 12473 12474 12475 12476 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 12489 12490 12491 12492 12493 12494 12495 12496 12497 12498 12499 12500 12501 12502 12503 12504 12505 12506 12507 12508 12509 12510 12511 12512 12513 12514 12515 12516 12517 12518 12519 12520 12521 12522 12523 12524 12525 12526 12527 12528 12529 12530 12531 12532 12533 | static Jim_Obj *ExprPop(struct JimExprState *e) { return e->stack[--e->stacklen]; } static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e) { int intresult = 1; int rc = JIM_OK; Jim_Obj *A = ExprPop(e); double dA, dC = 0; jim_wide wA, wC = 0; if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) { switch (e->opcode) { case JIM_EXPROP_FUNC_INT: case JIM_EXPROP_FUNC_WIDE: case JIM_EXPROP_FUNC_ROUND: case JIM_EXPROP_UNARYPLUS: wC = wA; break; case JIM_EXPROP_FUNC_DOUBLE: dC = wA; intresult = 0; break; case JIM_EXPROP_FUNC_ABS: wC = wA >= 0 ? wA : -wA; break; case JIM_EXPROP_UNARYMINUS: wC = -wA; break; case JIM_EXPROP_NOT: wC = !wA; break; default: abort(); } } else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) { switch (e->opcode) { case JIM_EXPROP_FUNC_INT: case JIM_EXPROP_FUNC_WIDE: wC = dA; break; case JIM_EXPROP_FUNC_ROUND: wC = dA < 0 ? (dA - 0.5) : (dA + 0.5); break; case JIM_EXPROP_FUNC_DOUBLE: case JIM_EXPROP_UNARYPLUS: dC = dA; intresult = 0; break; case JIM_EXPROP_FUNC_ABS: dC = dA >= 0 ? dA : -dA; intresult = 0; break; case JIM_EXPROP_UNARYMINUS: dC = -dA; intresult = 0; break; case JIM_EXPROP_NOT: wC = !dA; break; default: abort(); } } if (rc == JIM_OK) { |
︙ | ︙ | |||
12430 12431 12432 12433 12434 12435 12436 | return rc; } static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e) { | | < < | 12734 12735 12736 12737 12738 12739 12740 12741 12742 12743 12744 12745 12746 12747 12748 12749 12750 12751 12752 12753 12754 12755 12756 12757 12758 12759 12760 12761 | return rc; } static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e) { int intresult = 1; int rc = JIM_OK; double dA, dB, dC = 0; jim_wide wA, wB, wC = 0; Jim_Obj *B = ExprPop(e); Jim_Obj *A = ExprPop(e); if ((A->typePtr != &doubleObjType || A->bytes) && (B->typePtr != &doubleObjType || B->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) { switch (e->opcode) { case JIM_EXPROP_POW: case JIM_EXPROP_FUNC_POW: wC = JimPowWide(wA, wB); break; case JIM_EXPROP_ADD: wC = wA + wB; |
︙ | ︙ | |||
12499 12500 12501 12502 12503 12504 12505 12506 12507 12508 12509 12510 12511 12512 | wC = wA != wB; break; default: abort(); } } else if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) { switch (e->opcode) { case JIM_EXPROP_POW: case JIM_EXPROP_FUNC_POW: #ifdef JIM_MATH_FUNCTIONS dC = pow(dA, dB); #else Jim_SetResultString(interp, "unsupported", -1); | > | 12801 12802 12803 12804 12805 12806 12807 12808 12809 12810 12811 12812 12813 12814 12815 | wC = wA != wB; break; default: abort(); } } else if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) { intresult = 0; switch (e->opcode) { case JIM_EXPROP_POW: case JIM_EXPROP_FUNC_POW: #ifdef JIM_MATH_FUNCTIONS dC = pow(dA, dB); #else Jim_SetResultString(interp, "unsupported", -1); |
︙ | ︙ | |||
12564 12565 12566 12567 12568 12569 12570 | } else { int i = Jim_StringCompareObj(interp, A, B, 0); | < < | 12867 12868 12869 12870 12871 12872 12873 12874 12875 12876 12877 12878 12879 12880 | } else { int i = Jim_StringCompareObj(interp, A, B, 0); switch (e->opcode) { case JIM_EXPROP_LT: wC = i < 0; break; case JIM_EXPROP_GT: wC = i > 0; break; |
︙ | ︙ | |||
12613 12614 12615 12616 12617 12618 12619 | static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj) { int listlen; int i; listlen = Jim_ListLength(interp, listObjPtr); for (i = 0; i < listlen; i++) { | < < < < | | < < < | | | < < < < | 12914 12915 12916 12917 12918 12919 12920 12921 12922 12923 12924 12925 12926 12927 12928 12929 12930 12931 12932 12933 12934 12935 12936 12937 12938 12939 12940 12941 12942 12943 12944 12945 12946 12947 12948 12949 | static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj) { int listlen; int i; listlen = Jim_ListLength(interp, listObjPtr); for (i = 0; i < listlen; i++) { if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) { return 1; } } return 0; } static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e) { Jim_Obj *B = ExprPop(e); Jim_Obj *A = ExprPop(e); jim_wide wC; switch (e->opcode) { case JIM_EXPROP_STREQ: case JIM_EXPROP_STRNE: wC = Jim_StringEqObj(A, B); if (e->opcode == JIM_EXPROP_STRNE) { wC = !wC; } break; case JIM_EXPROP_STRIN: wC = JimSearchList(interp, B, A); break; case JIM_EXPROP_STRNI: wC = !JimSearchList(interp, B, A); break; default: |
︙ | ︙ | |||
12821 12822 12823 12824 12825 12826 12827 | { LAZY_NONE, LAZY_OP, LAZY_LEFT, LAZY_RIGHT }; | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | > > > > > | < | < | 13111 13112 13113 13114 13115 13116 13117 13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 13155 13156 13157 13158 13159 13160 13161 13162 13163 13164 13165 13166 13167 13168 13169 13170 13171 13172 13173 13174 13175 13176 13177 13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 13227 13228 13229 13230 13231 13232 13233 13234 13235 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 13248 13249 | { LAZY_NONE, LAZY_OP, LAZY_LEFT, LAZY_RIGHT }; #define OPRINIT(N, P, A, F) {N, F, P, A, LAZY_NONE, sizeof(N) - 1} #define OPRINIT_LAZY(N, P, A, F, L) {N, F, P, A, L, sizeof(N) - 1} static const struct Jim_ExprOperator Jim_ExprOperators[] = { OPRINIT("*", 110, 2, JimExprOpBin), OPRINIT("/", 110, 2, JimExprOpBin), OPRINIT("%", 110, 2, JimExprOpIntBin), OPRINIT("-", 100, 2, JimExprOpBin), OPRINIT("+", 100, 2, JimExprOpBin), OPRINIT("<<", 90, 2, JimExprOpIntBin), OPRINIT(">>", 90, 2, JimExprOpIntBin), OPRINIT("<<<", 90, 2, JimExprOpIntBin), OPRINIT(">>>", 90, 2, JimExprOpIntBin), OPRINIT("<", 80, 2, JimExprOpBin), OPRINIT(">", 80, 2, JimExprOpBin), OPRINIT("<=", 80, 2, JimExprOpBin), OPRINIT(">=", 80, 2, JimExprOpBin), OPRINIT("==", 70, 2, JimExprOpBin), OPRINIT("!=", 70, 2, JimExprOpBin), OPRINIT("&", 50, 2, JimExprOpIntBin), OPRINIT("^", 49, 2, JimExprOpIntBin), OPRINIT("|", 48, 2, JimExprOpIntBin), OPRINIT_LAZY("&&", 10, 2, NULL, LAZY_OP), OPRINIT_LAZY(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT), OPRINIT_LAZY(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT), OPRINIT_LAZY("||", 9, 2, NULL, LAZY_OP), OPRINIT_LAZY(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT), OPRINIT_LAZY(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT), OPRINIT_LAZY("?", 5, 2, JimExprOpNull, LAZY_OP), OPRINIT_LAZY(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT), OPRINIT_LAZY(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT), OPRINIT_LAZY(":", 5, 2, JimExprOpNull, LAZY_OP), OPRINIT_LAZY(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT), OPRINIT_LAZY(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT), OPRINIT("**", 250, 2, JimExprOpBin), OPRINIT("eq", 60, 2, JimExprOpStrBin), OPRINIT("ne", 60, 2, JimExprOpStrBin), OPRINIT("in", 55, 2, JimExprOpStrBin), OPRINIT("ni", 55, 2, JimExprOpStrBin), OPRINIT("!", 150, 1, JimExprOpNumUnary), OPRINIT("~", 150, 1, JimExprOpIntUnary), OPRINIT(NULL, 150, 1, JimExprOpNumUnary), OPRINIT(NULL, 150, 1, JimExprOpNumUnary), OPRINIT("int", 200, 1, JimExprOpNumUnary), OPRINIT("wide", 200, 1, JimExprOpNumUnary), OPRINIT("abs", 200, 1, JimExprOpNumUnary), OPRINIT("double", 200, 1, JimExprOpNumUnary), OPRINIT("round", 200, 1, JimExprOpNumUnary), OPRINIT("rand", 200, 0, JimExprOpNone), OPRINIT("srand", 200, 1, JimExprOpIntUnary), #ifdef JIM_MATH_FUNCTIONS OPRINIT("sin", 200, 1, JimExprOpDoubleUnary), OPRINIT("cos", 200, 1, JimExprOpDoubleUnary), OPRINIT("tan", 200, 1, JimExprOpDoubleUnary), OPRINIT("asin", 200, 1, JimExprOpDoubleUnary), OPRINIT("acos", 200, 1, JimExprOpDoubleUnary), OPRINIT("atan", 200, 1, JimExprOpDoubleUnary), OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary), OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary), OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary), OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary), OPRINIT("floor", 200, 1, JimExprOpDoubleUnary), OPRINIT("exp", 200, 1, JimExprOpDoubleUnary), OPRINIT("log", 200, 1, JimExprOpDoubleUnary), OPRINIT("log10", 200, 1, JimExprOpDoubleUnary), OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary), OPRINIT("pow", 200, 2, JimExprOpBin), #endif }; #undef OPRINIT #undef OPRINIT_LAZY #define JIM_EXPR_OPERATORS_NUM \ (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) static int JimParseExpression(struct JimParserCtx *pc) { while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) { if (*pc->p == '\n') { pc->linenr++; } pc->p++; pc->len--; } pc->tline = pc->linenr; pc->tstart = pc->p; if (pc->len == 0) { pc->tend = pc->p; pc->tt = JIM_TT_EOL; pc->eof = 1; return JIM_OK; } switch (*(pc->p)) { case '(': pc->tt = JIM_TT_SUBEXPR_START; goto singlechar; case ')': pc->tt = JIM_TT_SUBEXPR_END; goto singlechar; case ',': pc->tt = JIM_TT_SUBEXPR_COMMA; singlechar: pc->tend = pc->p; pc->p++; pc->len--; break; case '[': return JimParseCmd(pc); case '$': if (JimParseVar(pc) == JIM_ERR) |
︙ | ︙ | |||
12990 12991 12992 12993 12994 12995 12996 | break; } return JIM_OK; } static int JimParseExprNumber(struct JimParserCtx *pc) { | | < < < > < < < < < < | | < < < < < | < | < < < < | < < | < < < < < < < < < < < < < < < | < < > | | | | | < < | | | | 13285 13286 13287 13288 13289 13290 13291 13292 13293 13294 13295 13296 13297 13298 13299 13300 13301 13302 13303 13304 13305 13306 13307 13308 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 13330 13331 13332 | break; } return JIM_OK; } static int JimParseExprNumber(struct JimParserCtx *pc) { char *end; pc->tt = JIM_TT_EXPR_INT; jim_strtoull(pc->p, (char **)&pc->p); if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) { if (strtod(pc->tstart, &end)) { } if (end == pc->tstart) return JIM_ERR; if (end > pc->p) { pc->tt = JIM_TT_EXPR_DOUBLE; pc->p = end; } } pc->tend = pc->p - 1; pc->len -= (pc->p - pc->tstart); return JIM_OK; } static int JimParseExprIrrational(struct JimParserCtx *pc) { const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL }; int i; for (i = 0; irrationals[i]; i++) { const char *irr = irrationals[i]; if (strncmp(irr, pc->p, 3) == 0) { pc->p += 3; pc->len -= 3; pc->tend = pc->p - 1; pc->tt = JIM_TT_EXPR_DOUBLE; return JIM_OK; } } return JIM_ERR; } |
︙ | ︙ | |||
13105 13106 13107 13108 13109 13110 13111 | len--; p++; } if (*p != '(') { return JIM_ERR; } } | < < | 13362 13363 13364 13365 13366 13367 13368 13369 13370 13371 13372 13373 13374 13375 13376 13377 13378 | len--; p++; } if (*p != '(') { return JIM_ERR; } } pc->tend = pc->p + bestLen - 1; pc->p += bestLen; pc->len -= bestLen; pc->tt = bestIdx; return JIM_OK; } static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode) { |
︙ | ︙ | |||
13611 13612 13613 13614 13615 13616 13617 | ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, parser.tline); } #ifdef DEBUG_SHOW_EXPR_TOKENS { int i; | | > > > > > > | 13866 13867 13868 13869 13870 13871 13872 13873 13874 13875 13876 13877 13878 13879 13880 13881 13882 13883 13884 13885 13886 13887 13888 13889 13890 13891 13892 | ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, parser.tline); } #ifdef DEBUG_SHOW_EXPR_TOKENS { int i; printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj)); for (i = 0; i < tokenlist.count; i++) { printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type), tokenlist.list[i].len, tokenlist.list[i].token); } } #endif if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) { ScriptTokenListFree(&tokenlist); Jim_DecrRefCount(interp, fileNameObj); return JIM_ERR; } expr = ExprCreateByteCode(interp, &tokenlist, fileNameObj); ScriptTokenListFree(&tokenlist); |
︙ | ︙ | |||
13668 13669 13670 13671 13672 13673 13674 13675 13676 13677 13678 13679 13680 13681 | if (objPtr->typePtr != &exprObjType) { if (SetExprFromAny(interp, objPtr) != JIM_OK) { return NULL; } } return (ExprByteCode *) Jim_GetIntRepPtr(objPtr); } #define JIM_EE_STATICSTACK_LEN 10 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr) { ExprByteCode *expr; Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN]; | > > > > > > > > > > > > > > | 13929 13930 13931 13932 13933 13934 13935 13936 13937 13938 13939 13940 13941 13942 13943 13944 13945 13946 13947 13948 13949 13950 13951 13952 13953 13954 13955 13956 | if (objPtr->typePtr != &exprObjType) { if (SetExprFromAny(interp, objPtr) != JIM_OK) { return NULL; } } return (ExprByteCode *) Jim_GetIntRepPtr(objPtr); } #ifdef JIM_OPTIMIZATION static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token) { if (token->type == JIM_TT_EXPR_INT) return token->objPtr; else if (token->type == JIM_TT_VAR) return Jim_GetVariable(interp, token->objPtr, JIM_NONE); else if (token->type == JIM_TT_DICTSUGAR) return JimExpandDictSugar(interp, token->objPtr); else return NULL; } #endif #define JIM_EE_STATICSTACK_LEN 10 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr) { ExprByteCode *expr; Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN]; |
︙ | ︙ | |||
13691 13692 13693 13694 13695 13696 13697 | #ifdef JIM_OPTIMIZATION { Jim_Obj *objPtr; switch (expr->len) { case 1: | | | | < < < < < | < | < | | < | < | | | | < < < < < < | | | < < < < < < < < < < < < < < | < | | | | | | | | | | | | | | | | | | | | | | < | | | | | < < < > | 13966 13967 13968 13969 13970 13971 13972 13973 13974 13975 13976 13977 13978 13979 13980 13981 13982 13983 13984 13985 13986 13987 13988 13989 13990 13991 13992 13993 13994 13995 13996 13997 13998 13999 14000 14001 14002 14003 14004 14005 14006 14007 14008 14009 14010 14011 14012 14013 14014 14015 14016 14017 14018 14019 14020 14021 14022 14023 14024 14025 14026 14027 14028 14029 14030 14031 14032 14033 14034 14035 14036 14037 14038 | #ifdef JIM_OPTIMIZATION { Jim_Obj *objPtr; switch (expr->len) { case 1: objPtr = JimExprIntValOrVar(interp, &expr->token[0]); if (objPtr) { Jim_IncrRefCount(objPtr); *exprResultPtrPtr = objPtr; return JIM_OK; } break; case 2: if (expr->token[1].type == JIM_EXPROP_NOT) { objPtr = JimExprIntValOrVar(interp, &expr->token[0]); if (objPtr && JimIsWide(objPtr)) { *exprResultPtrPtr = JimWideValue(objPtr) ? interp->falseObj : interp->trueObj; Jim_IncrRefCount(*exprResultPtrPtr); return JIM_OK; } } break; case 3: objPtr = JimExprIntValOrVar(interp, &expr->token[0]); if (objPtr && JimIsWide(objPtr)) { Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, &expr->token[1]); if (objPtr2 && JimIsWide(objPtr2)) { jim_wide wideValueA = JimWideValue(objPtr); jim_wide wideValueB = JimWideValue(objPtr2); int cmpRes; switch (expr->token[2].type) { case JIM_EXPROP_LT: cmpRes = wideValueA < wideValueB; break; case JIM_EXPROP_LTE: cmpRes = wideValueA <= wideValueB; break; case JIM_EXPROP_GT: cmpRes = wideValueA > wideValueB; break; case JIM_EXPROP_GTE: cmpRes = wideValueA >= wideValueB; break; case JIM_EXPROP_NUMEQ: cmpRes = wideValueA == wideValueB; break; case JIM_EXPROP_NUMNE: cmpRes = wideValueA != wideValueB; break; default: goto noopt; } *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj; Jim_IncrRefCount(*exprResultPtrPtr); return JIM_OK; } } break; } } noopt: #endif expr->inUse++; if (expr->len > JIM_EE_STATICSTACK_LEN) |
︙ | ︙ | |||
13955 13956 13957 13958 13959 13960 13961 | JIM_NOTUSED(interp); memcpy(newVec, srcPtr->internalRep.ptr, size); dupPtr->internalRep.ptr = newVec; dupPtr->typePtr = &scanFmtStringObjType; } | | | < < < | 14197 14198 14199 14200 14201 14202 14203 14204 14205 14206 14207 14208 14209 14210 14211 14212 14213 | JIM_NOTUSED(interp); memcpy(newVec, srcPtr->internalRep.ptr, size); dupPtr->internalRep.ptr = newVec; dupPtr->typePtr = &scanFmtStringObjType; } static void UpdateStringOfScanFmt(Jim_Obj *objPtr) { JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep); } static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { ScanFmtStringObj *fmtObj; char *buffer; |
︙ | ︙ | |||
14174 14175 14176 14177 14178 14179 14180 | size_t anchor = pos; int i; Jim_Obj *tmpObj = NULL; *valObjPtr = 0; if (descr->prefix) { | < | 14413 14414 14415 14416 14417 14418 14419 14420 14421 14422 14423 14424 14425 14426 | size_t anchor = pos; int i; Jim_Obj *tmpObj = NULL; *valObjPtr = 0; if (descr->prefix) { for (i = 0; pos < strLen && descr->prefix[i]; ++i) { if (isspace(UCHAR(descr->prefix[i]))) while (pos < strLen && isspace(UCHAR(str[pos]))) ++pos; else if (descr->prefix[i] != str[pos]) break; |
︙ | ︙ | |||
14515 14516 14517 14518 14519 14520 14521 | return retcode; } static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv) { int retcode; | | > > > > > > > > > > > > > > > > | | | > > > | > < < > > > | 14753 14754 14755 14756 14757 14758 14759 14760 14761 14762 14763 14764 14765 14766 14767 14768 14769 14770 14771 14772 14773 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 14785 14786 14787 14788 14789 14790 14791 14792 14793 14794 14795 14796 14797 14798 14799 14800 14801 14802 14803 14804 14805 14806 14807 14808 14809 14810 14811 | return retcode; } static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv) { int retcode; Jim_Cmd *cmdPtr; #if 0 printf("invoke"); int j; for (j = 0; j < objc; j++) { printf(" '%s'", Jim_String(objv[j])); } printf("\n"); #endif if (interp->framePtr->tailcallCmd) { cmdPtr = interp->framePtr->tailcallCmd; interp->framePtr->tailcallCmd = NULL; } else { cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG); if (cmdPtr == NULL) { return JimUnknown(interp, objc, objv); } JimIncrCmdRefCount(cmdPtr); } if (interp->evalDepth == interp->maxEvalDepth) { Jim_SetResultString(interp, "Infinite eval recursion", -1); retcode = JIM_ERR; goto out; } interp->evalDepth++; Jim_SetEmptyResult(interp); if (cmdPtr->isproc) { retcode = JimCallProcedure(interp, cmdPtr, objc, objv); } else { interp->cmdPrivData = cmdPtr->u.native.privData; retcode = cmdPtr->u.native.cmdProc(interp, objc, objv); } interp->evalDepth--; out: JimDecrCmdRefCount(interp, cmdPtr); return retcode; } int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv) { int i, retcode; |
︙ | ︙ | |||
14571 14572 14573 14574 14575 14576 14577 | nargv[0] = prefix; memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc); ret = Jim_EvalObjVector(interp, objc + 1, nargv); Jim_Free(nargv); return ret; } | | < < | | < < < < < < | 14830 14831 14832 14833 14834 14835 14836 14837 14838 14839 14840 14841 14842 14843 14844 14845 14846 14847 14848 14849 14850 14851 14852 14853 14854 14855 14856 14857 14858 14859 14860 14861 14862 14863 14864 14865 14866 14867 14868 14869 14870 14871 14872 | nargv[0] = prefix; memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc); ret = Jim_EvalObjVector(interp, objc + 1, nargv); Jim_Free(nargv); return ret; } static void JimAddErrorToStack(Jim_Interp *interp, ScriptObj *script) { if (!interp->errorFlag) { interp->errorFlag = 1; Jim_IncrRefCount(script->fileNameObj); Jim_DecrRefCount(interp, interp->errorFileNameObj); interp->errorFileNameObj = script->fileNameObj; interp->errorLine = script->linenr; JimResetStackTrace(interp); interp->addStackTrace++; } if (interp->addStackTrace > 0) { JimAppendStackTrace(interp, Jim_String(interp->errorProc), script->fileNameObj, script->linenr); if (Jim_Length(script->fileNameObj)) { interp->addStackTrace = 0; } Jim_DecrRefCount(interp, interp->errorProc); interp->errorProc = interp->emptyObj; Jim_IncrRefCount(interp->errorProc); } } static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr) { Jim_Obj *objPtr; switch (token->type) { |
︙ | ︙ | |||
14720 14721 14722 14723 14724 14725 14726 14727 14728 14729 14730 14731 14732 14733 | && token[2].type == JIM_TT_VAR) { objPtr->typePtr = &interpolatedObjType; objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr; objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2]; Jim_IncrRefCount(intv[2]); } s = objPtr->bytes = Jim_Alloc(totlen + 1); objPtr->length = totlen; for (i = 0; i < tokens; i++) { if (intv[i]) { memcpy(s, intv[i]->bytes, intv[i]->length); s += intv[i]->length; | > > > > > | 14971 14972 14973 14974 14975 14976 14977 14978 14979 14980 14981 14982 14983 14984 14985 14986 14987 14988 14989 | && token[2].type == JIM_TT_VAR) { objPtr->typePtr = &interpolatedObjType; objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr; objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2]; Jim_IncrRefCount(intv[2]); } else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) { JimSetSourceInfo(interp, objPtr, intv[0]->internalRep.sourceValue.fileNameObj, intv[0]->internalRep.sourceValue.lineNumber); } s = objPtr->bytes = Jim_Alloc(totlen + 1); objPtr->length = totlen; for (i = 0; i < tokens; i++) { if (intv[i]) { memcpy(s, intv[i]->bytes, intv[i]->length); s += intv[i]->length; |
︙ | ︙ | |||
14743 14744 14745 14746 14747 14748 14749 14750 14751 14752 14753 14754 14755 14756 | return objPtr; } static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr) { int retcode = JIM_OK; if (listPtr->internalRep.listValue.len) { Jim_IncrRefCount(listPtr); retcode = JimInvokeCommand(interp, listPtr->internalRep.listValue.len, listPtr->internalRep.listValue.ele); Jim_DecrRefCount(interp, listPtr); | > > | 14999 15000 15001 15002 15003 15004 15005 15006 15007 15008 15009 15010 15011 15012 15013 15014 | return objPtr; } static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr) { int retcode = JIM_OK; JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list.")); if (listPtr->internalRep.listValue.len) { Jim_IncrRefCount(listPtr); retcode = JimInvokeCommand(interp, listPtr->internalRep.listValue.len, listPtr->internalRep.listValue.ele); Jim_DecrRefCount(interp, listPtr); |
︙ | ︙ | |||
14774 14775 14776 14777 14778 14779 14780 | Jim_Obj *prevScriptObj; if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) { return JimEvalObjList(interp, scriptObjPtr); } Jim_IncrRefCount(scriptObjPtr); | | > > > > | 15032 15033 15034 15035 15036 15037 15038 15039 15040 15041 15042 15043 15044 15045 15046 15047 15048 15049 15050 | Jim_Obj *prevScriptObj; if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) { return JimEvalObjList(interp, scriptObjPtr); } Jim_IncrRefCount(scriptObjPtr); script = JimGetScript(interp, scriptObjPtr); if (!JimScriptValid(interp, script)) { Jim_DecrRefCount(interp, scriptObjPtr); return JIM_ERR; } Jim_SetEmptyResult(interp); token = script->token; #ifdef JIM_OPTIMIZATION if (script->len == 0) { |
︙ | ︙ | |||
14937 14938 14939 14940 14941 14942 14943 | if (argv != sargv) { Jim_Free(argv); argv = sargv; } } | > | > > > > > > | 15199 15200 15201 15202 15203 15204 15205 15206 15207 15208 15209 15210 15211 15212 15213 15214 15215 15216 15217 15218 15219 15220 | if (argv != sargv) { Jim_Free(argv); argv = sargv; } } if (retcode == JIM_ERR) { JimAddErrorToStack(interp, script); } else if (retcode != JIM_RETURN || interp->returnCode != JIM_ERR) { interp->addStackTrace = 0; } interp->currentScriptObj = prevScriptObj; Jim_FreeIntRep(interp, scriptObjPtr); scriptObjPtr->typePtr = &scriptObjType; Jim_SetIntRepPtr(scriptObjPtr, script); |
︙ | ︙ | |||
15049 15050 15051 15052 15053 15054 15055 | else { retcode = Jim_EvalObj(interp, scriptObj); } interp->framePtr = interp->framePtr->parent; | < | < < < < < | 15318 15319 15320 15321 15322 15323 15324 15325 15326 15327 15328 15329 15330 15331 15332 15333 15334 15335 15336 15337 15338 15339 15340 15341 | else { retcode = Jim_EvalObj(interp, scriptObj); } interp->framePtr = interp->framePtr->parent; JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE); return retcode; } #endif static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv) { Jim_CallFrame *callFramePtr; int i, d, retcode, optargs; ScriptObj *script; if (argc - 1 < cmd->u.proc.reqArity || (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) { JimSetProcWrongArgs(interp, argv[0], cmd); return JIM_ERR; |
︙ | ︙ | |||
15094 15095 15096 15097 15098 15099 15100 | callFramePtr->argv = argv; callFramePtr->argc = argc; callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr; callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr; callFramePtr->staticVars = cmd->u.proc.staticVars; | | | 15357 15358 15359 15360 15361 15362 15363 15364 15365 15366 15367 15368 15369 15370 15371 | callFramePtr->argv = argv; callFramePtr->argc = argc; callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr; callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr; callFramePtr->staticVars = cmd->u.proc.staticVars; script = JimGetScript(interp, interp->currentScriptObj); callFramePtr->fileNameObj = script->fileNameObj; callFramePtr->line = script->linenr; Jim_IncrRefCount(cmd->u.proc.argListObjPtr); Jim_IncrRefCount(cmd->u.proc.bodyObjPtr); interp->framePtr = callFramePtr; |
︙ | ︙ | |||
15148 15149 15150 15151 15152 15153 15154 | } } retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr); badargset: | | < < < < | | > | | | > > > | | > > | | < | | > | | > | | > | > < < < | 15411 15412 15413 15414 15415 15416 15417 15418 15419 15420 15421 15422 15423 15424 15425 15426 15427 15428 15429 15430 15431 15432 15433 15434 15435 15436 15437 15438 15439 15440 15441 15442 15443 15444 15445 15446 15447 15448 15449 15450 15451 15452 15453 15454 15455 15456 15457 15458 15459 15460 15461 15462 15463 15464 15465 15466 15467 15468 15469 15470 15471 | } } retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr); badargset: interp->framePtr = interp->framePtr->parent; JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE); if (interp->framePtr->tailcallObj) { if (interp->framePtr->tailcall++ == 0) { do { Jim_Obj *tailcallObj = interp->framePtr->tailcallObj; interp->framePtr->tailcallObj = NULL; if (retcode == JIM_EVAL) { retcode = Jim_EvalObjList(interp, tailcallObj); if (retcode == JIM_RETURN) { interp->returnLevel++; } } Jim_DecrRefCount(interp, tailcallObj); } while (interp->framePtr->tailcallObj); if (interp->framePtr->tailcallCmd) { JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd); interp->framePtr->tailcallCmd = NULL; } } interp->framePtr->tailcall--; } if (retcode == JIM_RETURN) { if (--interp->returnLevel <= 0) { retcode = interp->returnCode; interp->returnCode = JIM_OK; interp->returnLevel = 0; } } else if (retcode == JIM_ERR) { interp->addStackTrace++; Jim_DecrRefCount(interp, interp->errorProc); interp->errorProc = argv[0]; Jim_IncrRefCount(interp->errorProc); } return retcode; } int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script) { int retval; Jim_Obj *scriptObjPtr; |
︙ | ︙ | |||
15264 15265 15266 15267 15268 15269 15270 | FILE *fp; char *buf; Jim_Obj *scriptObjPtr; Jim_Obj *prevScriptObj; struct stat sb; int retcode; int readlen; | < | 15529 15530 15531 15532 15533 15534 15535 15536 15537 15538 15539 15540 15541 15542 | FILE *fp; char *buf; Jim_Obj *scriptObjPtr; Jim_Obj *prevScriptObj; struct stat sb; int retcode; int readlen; if (stat(filename, &sb) != 0 || (fp = fopen(filename, "rt")) == NULL) { Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno)); return JIM_ERR; } if (sb.st_size == 0) { fclose(fp); |
︙ | ︙ | |||
15290 15291 15292 15293 15294 15295 15296 | fclose(fp); buf[readlen] = 0; scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen); JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1); Jim_IncrRefCount(scriptObjPtr); | < < < < < < < < < < < < < < < < < < < < < < < < < < | 15554 15555 15556 15557 15558 15559 15560 15561 15562 15563 15564 15565 15566 15567 | fclose(fp); buf[readlen] = 0; scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen); JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1); Jim_IncrRefCount(scriptObjPtr); prevScriptObj = interp->currentScriptObj; interp->currentScriptObj = scriptObjPtr; retcode = Jim_EvalObj(interp, scriptObjPtr); if (retcode == JIM_RETURN) { |
︙ | ︙ | |||
15478 15479 15480 15481 15482 15483 15484 | Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr); Jim_DecrRefCount(interp, objPtr); } typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type); | | | 15716 15717 15718 15719 15720 15721 15722 15723 15724 15725 15726 15727 15728 15729 15730 | Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr); Jim_DecrRefCount(interp, objPtr); } typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type); #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL) static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr, JimHashtableIteratorCallbackType *callback, int type) { Jim_HashEntry *he; Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); |
︙ | ︙ | |||
15513 15514 15515 15516 15517 15518 15519 | #define JIM_CMDLIST_COMMANDS 0 #define JIM_CMDLIST_PROCS 1 #define JIM_CMDLIST_CHANNELS 2 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type) { | | | 15751 15752 15753 15754 15755 15756 15757 15758 15759 15760 15761 15762 15763 15764 15765 | #define JIM_CMDLIST_COMMANDS 0 #define JIM_CMDLIST_PROCS 1 #define JIM_CMDLIST_CHANNELS 2 static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type) { Jim_Cmd *cmdPtr = Jim_GetHashEntryVal(he); Jim_Obj *objPtr; if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) { return; } |
︙ | ︙ | |||
15546 15547 15548 15549 15550 15551 15552 | #define JIM_VARLIST_VARS 2 #define JIM_VARLIST_VALUES 0x1000 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type) { | | | 15784 15785 15786 15787 15788 15789 15790 15791 15792 15793 15794 15795 15796 15797 15798 | #define JIM_VARLIST_VARS 2 #define JIM_VARLIST_VALUES 0x1000 static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type) { Jim_Var *varPtr = Jim_GetHashEntryVal(he); if (type != JIM_VARLIST_LOCALS || varPtr->linkFramePtr == NULL) { Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1)); if (type & JIM_VARLIST_VALUES) { Jim_ListAppendElement(interp, listObjPtr, varPtr->objPtr); } } |
︙ | ︙ | |||
15865 15866 15867 15868 15869 15870 15871 | ExprByteCode *expr; jim_wide stop, currentVal; Jim_Obj *objPtr; int cmpOffset; expr = JimGetExpression(interp, argv[2]); | | | | 16103 16104 16105 16106 16107 16108 16109 16110 16111 16112 16113 16114 16115 16116 16117 16118 16119 16120 | ExprByteCode *expr; jim_wide stop, currentVal; Jim_Obj *objPtr; int cmpOffset; expr = JimGetExpression(interp, argv[2]); incrScript = JimGetScript(interp, argv[3]); if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) { goto evalstart; } if (incrScript->token[1].type != JIM_TT_ESC || expr->token[0].type != JIM_TT_VAR || (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) { goto evalstart; |
︙ | ︙ | |||
16091 16092 16093 16094 16095 16096 16097 | { return iter->idx >= Jim_ListLength(interp, iter->objPtr); } static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap) { | | | 16329 16330 16331 16332 16333 16334 16335 16336 16337 16338 16339 16340 16341 16342 16343 | { return iter->idx >= Jim_ListLength(interp, iter->objPtr); } static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap) { int result = JIM_OK; int i, numargs; Jim_ListIter twoiters[2]; Jim_ListIter *iters; Jim_Obj *script; Jim_Obj *resultObj; if (argc < 4 || argc % 2 != 0) { |
︙ | ︙ | |||
16114 16115 16116 16117 16118 16119 16120 | } else { iters = Jim_Alloc(numargs * sizeof(*iters)); } for (i = 0; i < numargs; i++) { JimListIterInit(&iters[i], argv[i + 1]); if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) { | < | > > > > | 16352 16353 16354 16355 16356 16357 16358 16359 16360 16361 16362 16363 16364 16365 16366 16367 16368 16369 16370 16371 | } else { iters = Jim_Alloc(numargs * sizeof(*iters)); } for (i = 0; i < numargs; i++) { JimListIterInit(&iters[i], argv[i + 1]); if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) { result = JIM_ERR; } } if (result != JIM_OK) { Jim_SetResultString(interp, "foreach varlist is empty", -1); return result; } if (doMap) { resultObj = Jim_NewListObj(interp, NULL, 0); } else { resultObj = interp->emptyObj; |
︙ | ︙ | |||
16433 16434 16435 16436 16437 16438 16439 | static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr, *listObjPtr; int i; int idx; | | | | 16674 16675 16676 16677 16678 16679 16680 16681 16682 16683 16684 16685 16686 16687 16688 16689 | static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr, *listObjPtr; int i; int idx; if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?"); return JIM_ERR; } objPtr = argv[1]; Jim_IncrRefCount(objPtr); for (i = 2; i < argc; i++) { listObjPtr = objPtr; if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) { |
︙ | ︙ | |||
16550 16551 16552 16553 16554 16555 16556 | } if (commandObj) { Jim_IncrRefCount(commandObj); } listlen = Jim_ListLength(interp, argv[0]); for (i = 0; i < listlen; i++) { | < > < | 16791 16792 16793 16794 16795 16796 16797 16798 16799 16800 16801 16802 16803 16804 16805 16806 16807 | } if (commandObj) { Jim_IncrRefCount(commandObj); } listlen = Jim_ListLength(interp, argv[0]); for (i = 0; i < listlen; i++) { int eq = 0; Jim_Obj *objPtr = Jim_ListGetIndex(interp, argv[0], i); switch (opt_match) { case OPT_EXACT: eq = Jim_StringCompareObj(interp, argv[1], objPtr, opt_nocase) == 0; break; case OPT_GLOB: eq = Jim_StringMatchObj(interp, argv[1], objPtr, opt_nocase); |
︙ | ︙ | |||
16747 16748 16749 16750 16751 16752 16753 16754 16755 16756 16757 16758 | static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc < 3) { Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal"); return JIM_ERR; } else if (argc == 3) { if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK) return JIM_ERR; Jim_SetResult(interp, argv[2]); return JIM_OK; } | > | < < < | | > | > > > > > > | 16987 16988 16989 16990 16991 16992 16993 16994 16995 16996 16997 16998 16999 17000 17001 17002 17003 17004 17005 17006 17007 17008 17009 17010 17011 17012 17013 17014 17015 17016 17017 17018 17019 17020 17021 17022 17023 17024 17025 17026 17027 17028 17029 17030 17031 17032 17033 17034 17035 17036 17037 17038 17039 17040 17041 17042 17043 17044 17045 17046 17047 17048 17049 17050 17051 17052 17053 17054 17055 17056 17057 17058 17059 17060 17061 17062 17063 | static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc < 3) { Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal"); return JIM_ERR; } else if (argc == 3) { if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK) return JIM_ERR; Jim_SetResult(interp, argv[2]); return JIM_OK; } return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]); } static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[]) { static const char * const options[] = { "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", NULL }; enum { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE }; Jim_Obj *resObj; int i; int retCode; struct lsort_info info; if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "?options? list"); return JIM_ERR; } info.type = JIM_LSORT_ASCII; info.order = 1; info.indexed = 0; info.unique = 0; info.command = NULL; info.interp = interp; for (i = 1; i < (argc - 1); i++) { int option; if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) return JIM_ERR; switch (option) { case OPT_ASCII: info.type = JIM_LSORT_ASCII; break; case OPT_NOCASE: info.type = JIM_LSORT_NOCASE; break; case OPT_INTEGER: info.type = JIM_LSORT_INTEGER; break; case OPT_REAL: info.type = JIM_LSORT_REAL; break; case OPT_INCREASING: info.order = 1; break; case OPT_DECREASING: info.order = -1; break; case OPT_UNIQUE: info.unique = 1; break; case OPT_COMMAND: if (i >= (argc - 2)) { Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1); return JIM_ERR; } info.type = JIM_LSORT_COMMAND; info.command = argv[i + 1]; |
︙ | ︙ | |||
16845 16846 16847 16848 16849 16850 16851 | static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *stringObjPtr; int i; if (argc < 2) { | | | 17090 17091 17092 17093 17094 17095 17096 17097 17098 17099 17100 17101 17102 17103 17104 | static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *stringObjPtr; int i; if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?"); return JIM_ERR; } if (argc == 2) { stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); if (!stringObjPtr) return JIM_ERR; } |
︙ | ︙ | |||
16894 16895 16896 16897 16898 16899 16900 | static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int rc; if (argc < 2) { | | | 17139 17140 17141 17142 17143 17144 17145 17146 17147 17148 17149 17150 17151 17152 17153 | static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int rc; if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?"); return JIM_ERR; } if (argc == 2) { rc = Jim_EvalObj(interp, argv[1]); } else { |
︙ | ︙ | |||
16918 16919 16920 16921 16922 16923 16924 | static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc >= 2) { int retcode; Jim_CallFrame *savedCallFrame, *targetCallFrame; | | | < | > > > < < | < > | 17163 17164 17165 17166 17167 17168 17169 17170 17171 17172 17173 17174 17175 17176 17177 17178 17179 17180 17181 17182 17183 17184 17185 17186 17187 17188 17189 17190 17191 17192 17193 17194 17195 17196 17197 17198 17199 17200 17201 17202 17203 17204 17205 17206 17207 17208 17209 17210 17211 | static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc >= 2) { int retcode; Jim_CallFrame *savedCallFrame, *targetCallFrame; int savedTailcall; const char *str; savedCallFrame = interp->framePtr; str = Jim_String(argv[1]); if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') { targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]); argc--; argv++; } else { targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL); } if (targetCallFrame == NULL) { return JIM_ERR; } if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?"); return JIM_ERR; } interp->framePtr = targetCallFrame; savedTailcall = interp->framePtr->tailcall; interp->framePtr->tailcall = 0; if (argc == 2) { retcode = Jim_EvalObj(interp, argv[1]); } else { retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); } interp->framePtr->tailcall = savedTailcall; interp->framePtr = savedCallFrame; return retcode; } else { Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?"); return JIM_ERR; } |
︙ | ︙ | |||
17067 17068 17069 17070 17071 17072 17073 | } return JIM_RETURN; } static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { | > > > > > > > > > > > > > > > > > > > > > > | > > > | > > | | 17312 17313 17314 17315 17316 17317 17318 17319 17320 17321 17322 17323 17324 17325 17326 17327 17328 17329 17330 17331 17332 17333 17334 17335 17336 17337 17338 17339 17340 17341 17342 17343 17344 17345 17346 17347 17348 17349 17350 17351 17352 17353 17354 17355 17356 17357 17358 17359 17360 17361 17362 17363 17364 | } return JIM_RETURN; } static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (interp->framePtr->level == 0) { Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1); return JIM_ERR; } else if (argc >= 2) { Jim_CallFrame *cf = interp->framePtr->parent; Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG); if (cmdPtr == NULL) { return JIM_ERR; } JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd")); JimIncrCmdRefCount(cmdPtr); cf->tailcallCmd = cmdPtr; JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj")); cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1); Jim_IncrRefCount(cf->tailcallObj); return JIM_EVAL; } return JIM_OK; } static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *cmdList; Jim_Obj *prefixListObj = Jim_CmdPrivData(interp); cmdList = Jim_DuplicateObj(interp, prefixListObj); Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1); return JimEvalObjList(interp, cmdList); } static void JimAliasCmdDelete(Jim_Interp *interp, void *privData) { Jim_Obj *prefixListObj = privData; |
︙ | ︙ | |||
17156 17157 17158 17159 17160 17161 17162 17163 17164 17165 17166 17167 17168 17169 | return JIM_ERR; } static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int retcode; interp->local++; retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); interp->local--; | > > > > > | 17428 17429 17430 17431 17432 17433 17434 17435 17436 17437 17438 17439 17440 17441 17442 17443 17444 17445 17446 | return JIM_ERR; } static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int retcode; if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?"); return JIM_ERR; } interp->local++; retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); interp->local--; |
︙ | ︙ | |||
17352 17353 17354 17355 17356 17357 17358 | resultObjPtr = Jim_NewStringObj(interp, "", 0); while (strLen) { for (i = 0; i < numMaps; i += 2) { Jim_Obj *objPtr; const char *k; int kl; | | < | | 17629 17630 17631 17632 17633 17634 17635 17636 17637 17638 17639 17640 17641 17642 17643 17644 17645 17646 17647 17648 17649 17650 17651 17652 17653 17654 17655 | resultObjPtr = Jim_NewStringObj(interp, "", 0); while (strLen) { for (i = 0; i < numMaps; i += 2) { Jim_Obj *objPtr; const char *k; int kl; objPtr = Jim_ListGetIndex(interp, mapListObjPtr, i); k = Jim_String(objPtr); kl = Jim_Utf8Length(interp, objPtr); if (strLen >= kl && kl) { int rc; rc = JimStringCompareLen(str, k, kl, nocase); if (rc == 0) { if (noMatchStart) { Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart); noMatchStart = NULL; } Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1)); str += utf8_index(str, kl); strLen -= kl; break; } } } if (i == numMaps) { |
︙ | ︙ | |||
17394 17395 17396 17397 17398 17399 17400 | static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int len; int opt_case = 1; int option; static const char * const options[] = { "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace", | | | | 17670 17671 17672 17673 17674 17675 17676 17677 17678 17679 17680 17681 17682 17683 17684 17685 17686 17687 17688 17689 17690 | static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int len; int opt_case = 1; int option; static const char * const options[] = { "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "replace", "map", "repeat", "reverse", "index", "first", "last", "cat", "trim", "trimleft", "trimright", "tolower", "toupper", "totitle", NULL }; enum { OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_REPLACE, OPT_MAP, OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, OPT_CAT, OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER, OPT_TOTITLE }; static const char * const nocase_options[] = { "-nocase", NULL }; static const char * const nocase_length_options[] = { "-nocase", "-length", NULL |
︙ | ︙ | |||
17433 17434 17435 17436 17437 17438 17439 17440 17441 17442 17443 17444 17445 17446 | len = Jim_Utf8Length(interp, argv[2]); } else { len = Jim_Length(argv[2]); } Jim_SetResultInt(interp, len); return JIM_OK; case OPT_COMPARE: case OPT_EQUAL: { long opt_length = -1; int n = argc - 4; | > > > > > > > > > > > > > > > > > > > | 17709 17710 17711 17712 17713 17714 17715 17716 17717 17718 17719 17720 17721 17722 17723 17724 17725 17726 17727 17728 17729 17730 17731 17732 17733 17734 17735 17736 17737 17738 17739 17740 17741 | len = Jim_Utf8Length(interp, argv[2]); } else { len = Jim_Length(argv[2]); } Jim_SetResultInt(interp, len); return JIM_OK; case OPT_CAT:{ Jim_Obj *objPtr; if (argc == 3) { objPtr = argv[2]; } else { int i; objPtr = Jim_NewStringObj(interp, "", 0); for (i = 2; i < argc; i++) { Jim_AppendObj(interp, objPtr, argv[i]); } } Jim_SetResult(interp, objPtr); return JIM_OK; } case OPT_COMPARE: case OPT_EQUAL: { long opt_length = -1; int n = argc - 4; |
︙ | ︙ | |||
17849 17850 17851 17852 17853 17854 17855 17856 17857 17858 17859 | interp->signal_level += sig; if (Jim_CheckSignal(interp)) { exitCode = JIM_SIGNAL; } else { exitCode = Jim_EvalObj(interp, argv[0]); } interp->signal_level -= sig; | > > | | 18144 18145 18146 18147 18148 18149 18150 18151 18152 18153 18154 18155 18156 18157 18158 18159 18160 18161 18162 18163 18164 | interp->signal_level += sig; if (Jim_CheckSignal(interp)) { exitCode = JIM_SIGNAL; } else { exitCode = Jim_EvalObj(interp, argv[0]); interp->errorFlag = 0; } interp->signal_level -= sig; if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) { return exitCode; } if (sig && exitCode == JIM_SIGNAL) { if (interp->signal_set_result) { |
︙ | ︙ | |||
18007 18008 18009 18010 18011 18012 18013 | Jim_HashEntry *he; listObjPtr = Jim_NewListObj(interp, NULL, 0); JimInitHashTableIterator(&interp->references, &htiter); while ((he = Jim_NextHashEntry(&htiter)) != NULL) { char buf[JIM_REFERENCE_SPACE + 1]; | | | 18304 18305 18306 18307 18308 18309 18310 18311 18312 18313 18314 18315 18316 18317 18318 | Jim_HashEntry *he; listObjPtr = Jim_NewListObj(interp, NULL, 0); JimInitHashTableIterator(&interp->references, &htiter); while ((he = Jim_NextHashEntry(&htiter)) != NULL) { char buf[JIM_REFERENCE_SPACE + 1]; Jim_Reference *refPtr = Jim_GetHashEntryVal(he); const unsigned long *refId = he->key; JimFormatReference(buf, refPtr, *refId); Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1)); } Jim_SetResult(interp, listObjPtr); return JIM_OK; |
︙ | ︙ | |||
18041 18042 18043 18044 18045 18046 18047 | typedef void JimDictMatchCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type); static void JimDictMatchKeys(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type) { Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key); if (type & JIM_DICTMATCH_VALUES) { | | | 18338 18339 18340 18341 18342 18343 18344 18345 18346 18347 18348 18349 18350 18351 18352 | typedef void JimDictMatchCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type); static void JimDictMatchKeys(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_HashEntry *he, int type) { Jim_ListAppendElement(interp, listObjPtr, (Jim_Obj *)he->key); if (type & JIM_DICTMATCH_VALUES) { Jim_ListAppendElement(interp, listObjPtr, Jim_GetHashEntryVal(he)); } } static Jim_Obj *JimDictPatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr, JimDictMatchCallbackType *callback, int type) { Jim_HashEntry *he; |
︙ | ︙ | |||
18090 18091 18092 18093 18094 18095 18096 18097 18098 18099 18100 18101 18102 | { if (SetDictFromAny(interp, objPtr) != JIM_OK) { return -1; } return ((Jim_HashTable *)objPtr->internalRep.ptr)->used; } static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; int option; static const char * const options[] = { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > | | | | > | > | > > | > | > > > | | < < | < | < | < | < < | > | | < < | < < > | < < | > > > | > > > > > | 18387 18388 18389 18390 18391 18392 18393 18394 18395 18396 18397 18398 18399 18400 18401 18402 18403 18404 18405 18406 18407 18408 18409 18410 18411 18412 18413 18414 18415 18416 18417 18418 18419 18420 18421 18422 18423 18424 18425 18426 18427 18428 18429 18430 18431 18432 18433 18434 18435 18436 18437 18438 18439 18440 18441 18442 18443 18444 18445 18446 18447 18448 18449 18450 18451 18452 18453 18454 18455 18456 18457 18458 18459 18460 18461 18462 18463 18464 18465 18466 18467 18468 18469 18470 18471 18472 18473 18474 18475 18476 18477 18478 18479 18480 18481 18482 18483 18484 18485 18486 18487 18488 18489 18490 18491 18492 18493 18494 18495 18496 18497 18498 18499 18500 18501 18502 18503 18504 18505 18506 18507 18508 18509 18510 18511 18512 18513 18514 18515 18516 18517 18518 18519 18520 18521 18522 18523 18524 18525 18526 18527 18528 18529 18530 18531 18532 18533 18534 18535 18536 18537 18538 18539 18540 18541 18542 18543 18544 18545 18546 18547 18548 18549 18550 18551 18552 18553 18554 18555 18556 18557 18558 18559 18560 18561 18562 18563 | { if (SetDictFromAny(interp, objPtr) != JIM_OK) { return -1; } return ((Jim_HashTable *)objPtr->internalRep.ptr)->used; } int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr) { Jim_HashTable *ht; unsigned int i; if (SetDictFromAny(interp, objPtr) != JIM_OK) { return JIM_ERR; } ht = (Jim_HashTable *)objPtr->internalRep.ptr; printf("%d entries in table, %d buckets\n", ht->used, ht->size); for (i = 0; i < ht->size; i++) { Jim_HashEntry *he = ht->table[i]; if (he) { printf("%d: ", i); while (he) { printf(" %s", Jim_String(he->key)); he = he->next; } printf("\n"); } } return JIM_OK; } static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv) { Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1); Jim_AppendString(interp, prefixObj, " ", 1); Jim_AppendString(interp, prefixObj, subcmd, -1); return Jim_EvalObjPrefix(interp, prefixObj, argc, argv); } static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; int option; static const char * const options[] = { "create", "get", "set", "unset", "exists", "keys", "size", "info", "merge", "with", "append", "lappend", "incr", "remove", "values", "for", "replace", "update", NULL }; enum { OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXISTS, OPT_KEYS, OPT_SIZE, OPT_INFO, OPT_MERGE, OPT_WITH, OPT_APPEND, OPT_LAPPEND, OPT_INCR, OPT_REMOVE, OPT_VALUES, OPT_FOR, OPT_REPLACE, OPT_UPDATE, }; if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?"); return JIM_ERR; } if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) { return JIM_ERR; } switch (option) { case OPT_GET: if (argc < 3) { Jim_WrongNumArgs(interp, 2, argv, "dictionary ?key ...?"); return JIM_ERR; } if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG) != JIM_OK) { return JIM_ERR; } Jim_SetResult(interp, objPtr); return JIM_OK; case OPT_SET: if (argc < 5) { Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value"); return JIM_ERR; } return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG); case OPT_EXISTS: if (argc < 4) { Jim_WrongNumArgs(interp, 2, argv, "dictionary key ?key ...?"); return JIM_ERR; } else { int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG); if (rc < 0) { return JIM_ERR; } Jim_SetResultBool(interp, rc == JIM_OK); return JIM_OK; } case OPT_UNSET: if (argc < 4) { Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?"); return JIM_ERR; } if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, 0) != JIM_OK) { return JIM_ERR; } return JIM_OK; case OPT_KEYS: if (argc != 3 && argc != 4) { Jim_WrongNumArgs(interp, 2, argv, "dictionary ?pattern?"); return JIM_ERR; } return Jim_DictKeys(interp, argv[2], argc == 4 ? argv[3] : NULL); case OPT_SIZE: if (argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "dictionary"); return JIM_ERR; } else if (Jim_DictSize(interp, argv[2]) < 0) { return JIM_ERR; } Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2])); return JIM_OK; case OPT_MERGE: if (argc == 2) { return JIM_OK; } if (Jim_DictSize(interp, argv[2]) < 0) { return JIM_ERR; } break; case OPT_UPDATE: if (argc < 6 || argc % 2) { argc = 2; } break; case OPT_CREATE: if (argc % 2) { Jim_WrongNumArgs(interp, 2, argv, "?key value ...?"); return JIM_ERR; } objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2); Jim_SetResult(interp, objPtr); return JIM_OK; case OPT_INFO: if (argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "dictionary"); return JIM_ERR; } return Jim_DictInfo(interp, argv[2]); } return Jim_EvalEnsemble(interp, "dict", options[option], argc - 2, argv + 2); } static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { static const char * const options[] = { "-nobackslashes", "-nocommands", "-novariables", NULL |
︙ | ︙ | |||
18265 18266 18267 18268 18269 18270 18271 | "script", "source", "stacktrace", "nameofexecutable", "returncodes", "references", "alias", NULL }; enum { INFO_BODY, INFO_STATICS, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL, INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS, INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE, | | | 18611 18612 18613 18614 18615 18616 18617 18618 18619 18620 18621 18622 18623 18624 18625 | "script", "source", "stacktrace", "nameofexecutable", "returncodes", "references", "alias", NULL }; enum { INFO_BODY, INFO_STATICS, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL, INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS, INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE, INFO_RETURNCODES, INFO_REFERENCES, INFO_ALIAS, }; #ifdef jim_ext_namespace int nons = 0; if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) { |
︙ | ︙ | |||
18365 18366 18367 18368 18369 18370 18371 | break; case INFO_SCRIPT: if (argc != 2) { Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } | | | | | > > > > > > > > | | | | | | | | | | | | | | | | > | 18711 18712 18713 18714 18715 18716 18717 18718 18719 18720 18721 18722 18723 18724 18725 18726 18727 18728 18729 18730 18731 18732 18733 18734 18735 18736 18737 18738 18739 18740 18741 18742 18743 18744 18745 18746 18747 18748 18749 18750 18751 18752 18753 18754 18755 18756 18757 18758 18759 18760 18761 | break; case INFO_SCRIPT: if (argc != 2) { Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } Jim_SetResult(interp, JimGetScript(interp, interp->currentScriptObj)->fileNameObj); break; case INFO_SOURCE:{ jim_wide line; Jim_Obj *resObjPtr; Jim_Obj *fileNameObj; if (argc != 3 && argc != 5) { Jim_WrongNumArgs(interp, 2, argv, "source ?filename line?"); return JIM_ERR; } if (argc == 5) { if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) { return JIM_ERR; } resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2])); JimSetSourceInfo(interp, resObjPtr, argv[3], line); } else { if (argv[2]->typePtr == &sourceObjType) { fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj; line = argv[2]->internalRep.sourceValue.lineNumber; } else if (argv[2]->typePtr == &scriptObjType) { ScriptObj *script = JimGetScript(interp, argv[2]); fileNameObj = script->fileNameObj; line = script->firstline; } else { fileNameObj = interp->emptyObj; line = 1; } resObjPtr = Jim_NewListObj(interp, NULL, 0); Jim_ListAppendElement(interp, resObjPtr, fileNameObj); Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line)); } Jim_SetResult(interp, resObjPtr); break; } case INFO_STACKTRACE: Jim_SetResult(interp, interp->stackTrace); break; |
︙ | ︙ | |||
19045 19046 19047 19048 19049 19050 19051 | Jim_SetResultInt(interp, min+r); return JIM_OK; } } static const struct { const char *name; | | | 19400 19401 19402 19403 19404 19405 19406 19407 19408 19409 19410 19411 19412 19413 19414 | Jim_SetResultInt(interp, min+r); return JIM_OK; } } static const struct { const char *name; Jim_CmdProc *cmdProc; } Jim_CoreCommandsTable[] = { {"alias", Jim_AliasCoreCommand}, {"set", Jim_SetCoreCommand}, {"unset", Jim_UnsetCoreCommand}, {"puts", Jim_PutsCoreCommand}, {"+", Jim_AddCoreCommand}, {"*", Jim_MulCoreCommand}, |
︙ | ︙ | |||
19277 19278 19279 19280 19281 19282 19283 19284 19285 19286 19287 19288 19289 19290 | n++; extra += l; } len += extra; buf = Jim_Alloc(len + 1); len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]); Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len)); } #ifndef jim_ext_package int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags) | > > | 19632 19633 19634 19635 19636 19637 19638 19639 19640 19641 19642 19643 19644 19645 19646 19647 | n++; extra += l; } len += extra; buf = Jim_Alloc(len + 1); len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]); va_end(args); Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len)); } #ifndef jim_ext_package int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags) |
︙ | ︙ | |||
19528 19529 19530 19531 19532 19533 19534 | } } #include <ctype.h> #include <string.h> | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | < | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > < < < > | > | | > > > | | | | | | | | | | 19885 19886 19887 19888 19889 19890 19891 19892 19893 19894 19895 19896 19897 19898 19899 19900 19901 19902 19903 19904 19905 19906 19907 19908 19909 19910 19911 19912 19913 19914 19915 19916 19917 19918 19919 19920 19921 19922 19923 19924 19925 19926 19927 19928 19929 19930 19931 19932 19933 19934 19935 19936 19937 19938 19939 19940 19941 19942 19943 19944 19945 19946 19947 19948 19949 19950 19951 19952 19953 19954 19955 19956 19957 19958 19959 19960 19961 19962 19963 19964 19965 19966 19967 19968 19969 19970 19971 19972 19973 19974 19975 19976 19977 19978 19979 19980 19981 19982 19983 19984 19985 19986 19987 19988 19989 19990 19991 19992 19993 19994 19995 19996 19997 19998 19999 20000 20001 20002 20003 20004 20005 20006 20007 20008 20009 20010 20011 20012 20013 20014 20015 20016 20017 20018 20019 20020 20021 20022 20023 20024 20025 20026 20027 20028 20029 20030 20031 20032 20033 20034 20035 20036 20037 20038 20039 20040 20041 20042 20043 20044 20045 20046 20047 20048 20049 20050 20051 20052 20053 20054 20055 20056 20057 20058 20059 20060 20061 20062 20063 20064 20065 20066 20067 20068 20069 20070 20071 20072 20073 20074 20075 20076 20077 20078 20079 20080 20081 20082 20083 20084 20085 20086 20087 20088 20089 20090 20091 20092 20093 20094 20095 20096 20097 20098 20099 20100 20101 20102 20103 20104 20105 20106 20107 20108 20109 20110 20111 20112 20113 20114 20115 20116 20117 20118 20119 20120 20121 20122 20123 20124 20125 20126 20127 20128 20129 20130 20131 20132 20133 20134 20135 20136 20137 20138 20139 20140 20141 20142 20143 20144 20145 20146 20147 20148 20149 20150 20151 20152 20153 20154 20155 20156 20157 20158 20159 20160 20161 20162 20163 20164 20165 20166 20167 20168 20169 20170 20171 20172 20173 20174 20175 20176 20177 20178 20179 20180 20181 20182 20183 20184 20185 20186 20187 20188 20189 20190 20191 20192 20193 20194 20195 20196 20197 20198 20199 20200 20201 20202 20203 20204 20205 20206 20207 20208 20209 20210 20211 20212 20213 20214 20215 20216 20217 20218 20219 20220 20221 20222 20223 20224 20225 20226 20227 20228 20229 20230 20231 20232 20233 20234 20235 20236 20237 20238 20239 20240 20241 20242 20243 20244 20245 20246 20247 20248 20249 20250 20251 20252 20253 20254 20255 20256 20257 20258 20259 20260 20261 20262 20263 20264 20265 20266 20267 20268 20269 20270 20271 20272 20273 20274 20275 20276 20277 20278 20279 20280 20281 20282 20283 20284 20285 20286 20287 20288 20289 20290 20291 20292 20293 20294 20295 20296 20297 20298 20299 20300 20301 20302 20303 20304 20305 20306 20307 20308 20309 20310 20311 20312 20313 20314 20315 20316 20317 20318 20319 20320 20321 20322 20323 20324 20325 20326 20327 20328 20329 20330 20331 20332 20333 20334 20335 20336 20337 20338 20339 20340 20341 20342 20343 20344 20345 20346 20347 | } } #include <ctype.h> #include <string.h> #define JIM_INTEGER_SPACE 24 #define MAX_FLOAT_WIDTH 320 Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv) { const char *span, *format, *formatEnd, *msg; int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0; static const char * const mixedXPG = "cannot mix \"%\" and \"%n$\" conversion specifiers"; static const char * const badIndex[2] = { "not enough arguments for all format specifiers", "\"%n$\" argument index out of range" }; int formatLen; Jim_Obj *resultPtr; char *num_buffer = NULL; int num_buffer_size = 0; span = format = Jim_GetString(fmtObjPtr, &formatLen); formatEnd = format + formatLen; resultPtr = Jim_NewEmptyStringObj(interp); while (format != formatEnd) { char *end; int gotMinus, sawFlag; int gotPrecision, useShort; long width, precision; int newXpg; int ch; int step; int doubleType; char pad = ' '; char spec[2*JIM_INTEGER_SPACE + 12]; char *p; int formatted_chars; int formatted_bytes; const char *formatted_buf; step = utf8_tounicode(format, &ch); format += step; if (ch != '%') { numBytes += step; continue; } if (numBytes) { Jim_AppendString(interp, resultPtr, span, numBytes); numBytes = 0; } step = utf8_tounicode(format, &ch); if (ch == '%') { span = format; numBytes = step; format += step; continue; } newXpg = 0; if (isdigit(ch)) { int position = strtoul(format, &end, 10); if (*end == '$') { newXpg = 1; objIndex = position - 1; format = end + 1; step = utf8_tounicode(format, &ch); } } if (newXpg) { if (gotSequential) { msg = mixedXPG; goto errorMsg; } gotXpg = 1; } else { if (gotXpg) { msg = mixedXPG; goto errorMsg; } gotSequential = 1; } if ((objIndex < 0) || (objIndex >= objc)) { msg = badIndex[gotXpg]; goto errorMsg; } p = spec; *p++ = '%'; gotMinus = 0; sawFlag = 1; do { switch (ch) { case '-': gotMinus = 1; break; case '0': pad = ch; break; case ' ': case '+': case '#': break; default: sawFlag = 0; continue; } *p++ = ch; format += step; step = utf8_tounicode(format, &ch); } while (sawFlag); width = 0; if (isdigit(ch)) { width = strtoul(format, &end, 10); format = end; step = utf8_tounicode(format, &ch); } else if (ch == '*') { if (objIndex >= objc - 1) { msg = badIndex[gotXpg]; goto errorMsg; } if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) { goto error; } if (width < 0) { width = -width; if (!gotMinus) { *p++ = '-'; gotMinus = 1; } } objIndex++; format += step; step = utf8_tounicode(format, &ch); } gotPrecision = precision = 0; if (ch == '.') { gotPrecision = 1; format += step; step = utf8_tounicode(format, &ch); } if (isdigit(ch)) { precision = strtoul(format, &end, 10); format = end; step = utf8_tounicode(format, &ch); } else if (ch == '*') { if (objIndex >= objc - 1) { msg = badIndex[gotXpg]; goto errorMsg; } if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) { goto error; } if (precision < 0) { precision = 0; } objIndex++; format += step; step = utf8_tounicode(format, &ch); } useShort = 0; if (ch == 'h') { useShort = 1; format += step; step = utf8_tounicode(format, &ch); } else if (ch == 'l') { format += step; step = utf8_tounicode(format, &ch); if (ch == 'l') { format += step; step = utf8_tounicode(format, &ch); } } format += step; span = format; if (ch == 'i') { ch = 'd'; } doubleType = 0; switch (ch) { case '\0': msg = "format string ended in middle of field specifier"; goto errorMsg; case 's': { formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes); formatted_chars = Jim_Utf8Length(interp, objv[objIndex]); if (gotPrecision && (precision < formatted_chars)) { formatted_chars = precision; formatted_bytes = utf8_index(formatted_buf, precision); } break; } case 'c': { jim_wide code; if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) { goto error; } formatted_bytes = utf8_getchars(spec, code); formatted_buf = spec; formatted_chars = 1; break; } case 'b': { unsigned jim_wide w; int length; int i; int j; if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) { goto error; } length = sizeof(w) * 8; if (num_buffer_size < length + 1) { num_buffer_size = length + 1; num_buffer = Jim_Realloc(num_buffer, num_buffer_size); } j = 0; for (i = length; i > 0; ) { i--; if (w & ((unsigned jim_wide)1 << i)) { num_buffer[j++] = '1'; } else if (j || i == 0) { num_buffer[j++] = '0'; } } num_buffer[j] = 0; formatted_chars = formatted_bytes = j; formatted_buf = num_buffer; break; } case 'e': case 'E': case 'f': case 'g': case 'G': doubleType = 1; case 'd': case 'u': case 'o': case 'x': case 'X': { jim_wide w; double d; int length; if (width) { p += sprintf(p, "%ld", width); } if (gotPrecision) { p += sprintf(p, ".%ld", precision); } if (doubleType) { if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) { goto error; } length = MAX_FLOAT_WIDTH; } else { if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) { goto error; } length = JIM_INTEGER_SPACE; if (useShort) { if (ch == 'd') { w = (short)w; } else { w = (unsigned short)w; } } *p++ = 'l'; #ifdef HAVE_LONG_LONG if (sizeof(long long) == sizeof(jim_wide)) { *p++ = 'l'; } #endif } *p++ = (char) ch; *p = '\0'; if (width > length) { length = width; } if (gotPrecision) { length += precision; } if (num_buffer_size < length + 1) { num_buffer_size = length + 1; num_buffer = Jim_Realloc(num_buffer, num_buffer_size); } if (doubleType) { snprintf(num_buffer, length + 1, spec, d); } else { formatted_bytes = snprintf(num_buffer, length + 1, spec, w); } formatted_chars = formatted_bytes = strlen(num_buffer); formatted_buf = num_buffer; break; } default: { spec[0] = ch; spec[1] = '\0'; Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec); goto error; } } if (!gotMinus) { while (formatted_chars < width) { Jim_AppendString(interp, resultPtr, &pad, 1); formatted_chars++; } } Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes); while (formatted_chars < width) { Jim_AppendString(interp, resultPtr, &pad, 1); formatted_chars++; } objIndex += gotSequential; } if (numBytes) { Jim_AppendString(interp, resultPtr, span, numBytes); } Jim_Free(num_buffer); return resultPtr; errorMsg: Jim_SetResultString(interp, msg, -1); error: Jim_FreeNewObj(interp, resultPtr); Jim_Free(num_buffer); return NULL; } #if defined(JIM_REGEXP) #include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #define REG_MAX_PAREN 100 #define END 0 #define BOL 1 #define EOL 2 #define ANY 3 #define ANYOF 4 #define ANYBUT 5 #define BRANCH 6 #define BACK 7 #define EXACTLY 8 #define NOTHING 9 #define REP 10 #define REPMIN 11 #define REPX 12 #define REPXMIN 13 #define WORDA 15 #define WORDZ 16 #define OPENNC 1000 #define OPEN 1001 #define CLOSENC 2000 #define CLOSE 2001 #define CLOSE_END (CLOSE+REG_MAX_PAREN) #define REG_MAGIC 0xFADED00D #define OP(preg, p) (preg->program[p]) #define NEXT(preg, p) (preg->program[p + 1]) #define OPERAND(p) ((p) + 2) #define FAIL(R,M) { (R)->err = (M); return (M); } #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{') #define META "^$.[()|?{+*" #define HASWIDTH 1 #define SIMPLE 2 #define SPSTART 4 #define WORST 0 #define MAX_REP_COUNT 1000000 static int reg(regex_t *preg, int paren , int *flagp ); static int regpiece(regex_t *preg, int *flagp ); static int regbranch(regex_t *preg, int *flagp ); static int regatom(regex_t *preg, int *flagp ); static int regnode(regex_t *preg, int op ); static int regnext(regex_t *preg, int p ); static void regc(regex_t *preg, int b ); static int reginsert(regex_t *preg, int op, int size, int opnd ); static void regtail(regex_t *preg, int p, int val); static void regoptail(regex_t *preg, int p, int val ); static int regopsize(regex_t *preg, int p ); static int reg_range_find(const int *string, int c); static const char *str_find(const char *string, int c, int nocase); static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase); #ifdef DEBUG |
︙ | ︙ | |||
19983 19984 19985 19986 19987 19988 19989 | if (exp == NULL) FAIL(preg, REG_ERR_NULL_ARGUMENT); preg->cflags = cflags; preg->regparse = exp; | < < < | 20374 20375 20376 20377 20378 20379 20380 20381 20382 20383 20384 20385 20386 20387 | if (exp == NULL) FAIL(preg, REG_ERR_NULL_ARGUMENT); preg->cflags = cflags; preg->regparse = exp; preg->proglen = (strlen(exp) + 1) * 5; preg->program = malloc(preg->proglen * sizeof(int)); if (preg->program == NULL) FAIL(preg, REG_ERR_NOMEM); |
︙ | ︙ | |||
20150 20151 20152 20153 20154 20155 20156 | static int regpiece(regex_t *preg, int *flagp) { int ret; char op; int next; int flags; | < | 20538 20539 20540 20541 20542 20543 20544 20545 20546 20547 20548 20549 20550 20551 | static int regpiece(regex_t *preg, int *flagp) { int ret; char op; int next; int flags; int min; int max; ret = regatom(preg, &flags); if (ret == 0) return 0; |
︙ | ︙ | |||
20233 20234 20235 20236 20237 20238 20239 | preg->regparse++; if (ISMULT(*preg->regparse)) { preg->err = REG_ERR_NESTED_COUNT; return 0; } | | | 20620 20621 20622 20623 20624 20625 20626 20627 20628 20629 20630 20631 20632 20633 20634 | preg->regparse++; if (ISMULT(*preg->regparse)) { preg->err = REG_ERR_NESTED_COUNT; return 0; } return ret; } static void reg_addrange(regex_t *preg, int lower, int upper) { if (lower > upper) { reg_addrange(preg, upper, lower); } |
︙ | ︙ | |||
20327 20328 20329 20330 20331 20332 20333 20334 20335 20336 20337 20338 20339 20340 | s += n; } break; case 'U': if ((n = parse_hex(s, 8, ch)) > 0) { s += n; } case 'x': if ((n = parse_hex(s, 2, ch)) > 0) { s += n; } break; case '\0': s--; | > | 20714 20715 20716 20717 20718 20719 20720 20721 20722 20723 20724 20725 20726 20727 20728 | s += n; } break; case 'U': if ((n = parse_hex(s, 8, ch)) > 0) { s += n; } break; case 'x': if ((n = parse_hex(s, 2, ch)) > 0) { s += n; } break; case '\0': s--; |
︙ | ︙ | |||
20575 20576 20577 20578 20579 20580 20581 20582 20583 20584 20585 20586 20587 20588 | } static int regnode(regex_t *preg, int op) { reg_grow(preg, 2); preg->program[preg->p++] = op; preg->program[preg->p++] = 0; return preg->p - 2; } | > | 20963 20964 20965 20966 20967 20968 20969 20970 20971 20972 20973 20974 20975 20976 20977 | } static int regnode(regex_t *preg, int op) { reg_grow(preg, 2); preg->program[preg->p++] = op; preg->program[preg->p++] = 0; return preg->p - 2; } |
︙ | ︙ | |||
20604 20605 20606 20607 20608 20609 20610 | preg->program[opnd] = op; preg->p += size; return opnd + size; } | | | 20993 20994 20995 20996 20997 20998 20999 21000 21001 21002 21003 21004 21005 21006 21007 | preg->program[opnd] = op; preg->p += size; return opnd + size; } static void regtail(regex_t *preg, int p, int val) { int scan; int temp; int offset; scan = p; |
︙ | ︙ | |||
20667 20668 20669 20670 20671 20672 20673 | preg->eflags = eflags; preg->pmatch = pmatch; preg->nmatch = nmatch; preg->start = string; | | | < < < < < | | < < < < | < < < < < < < < < < < | 21056 21057 21058 21059 21060 21061 21062 21063 21064 21065 21066 21067 21068 21069 21070 21071 21072 21073 21074 21075 | preg->eflags = eflags; preg->pmatch = pmatch; preg->nmatch = nmatch; preg->start = string; for (scan = OPERAND(1); scan != 0; scan += regopsize(preg, scan)) { int op = OP(preg, scan); if (op == END) break; if (op == REPX || op == REPXMIN) preg->program[scan + 4] = 0; } if (preg->regmust != 0) { s = string; while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) { if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) { |
︙ | ︙ | |||
20948 20949 20950 20951 20952 20953 20954 20955 20956 20957 20958 20959 20960 20961 | } static int regmatch(regex_t *preg, int prog) { int scan; int next; scan = prog; #ifdef DEBUG if (scan != 0 && regnarrate) fprintf(stderr, "%s(\n", regprop(scan)); #endif | > | 21317 21318 21319 21320 21321 21322 21323 21324 21325 21326 21327 21328 21329 21330 21331 | } static int regmatch(regex_t *preg, int prog) { int scan; int next; const char *save; scan = prog; #ifdef DEBUG if (scan != 0 && regnarrate) fprintf(stderr, "%s(\n", regprop(scan)); #endif |
︙ | ︙ | |||
21036 21037 21038 21039 21040 21041 21042 | } preg->reginput += n; break; case NOTHING: break; case BACK: break; | | < < | | | | | | | | | | | | | < | < | < < < < < < < | | < > | | 21406 21407 21408 21409 21410 21411 21412 21413 21414 21415 21416 21417 21418 21419 21420 21421 21422 21423 21424 21425 21426 21427 21428 21429 21430 21431 21432 21433 21434 21435 21436 21437 21438 21439 21440 21441 21442 21443 21444 21445 21446 21447 21448 21449 21450 21451 21452 21453 21454 21455 21456 21457 21458 21459 21460 21461 21462 21463 21464 21465 21466 21467 21468 21469 | } preg->reginput += n; break; case NOTHING: break; case BACK: break; case BRANCH: if (OP(preg, next) != BRANCH) next = OPERAND(scan); else { do { save = preg->reginput; if (regmatch(preg, OPERAND(scan))) { return(1); } preg->reginput = save; scan = regnext(preg, scan); } while (scan != 0 && OP(preg, scan) == BRANCH); return(0); } break; case REP: case REPMIN: return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN); case REPX: case REPXMIN: return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN); case END: return 1; case OPENNC: case CLOSENC: return regmatch(preg, next); default: if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) { save = preg->reginput; if (regmatch(preg, next)) { if (OP(preg, scan) < CLOSE) { int no = OP(preg, scan) - OPEN; if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) { preg->pmatch[no].rm_so = save - preg->start; } } else { int no = OP(preg, scan) - CLOSE; if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) { preg->pmatch[no].rm_eo = save - preg->start; } } return(1); } return(0); } return REG_ERR_INTERNAL; } scan = next; } |
︙ | ︙ | |||
21179 21180 21181 21182 21183 21184 21185 21186 21187 21188 21189 21190 21191 21192 | return 0; if (OP(preg, p) == BACK) return(p-offset); else return(p+offset); } size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) { static const char *error_strings[] = { "success", "no match", | > > > > > > > > > > > > > > > > > > > > > > | 21538 21539 21540 21541 21542 21543 21544 21545 21546 21547 21548 21549 21550 21551 21552 21553 21554 21555 21556 21557 21558 21559 21560 21561 21562 21563 21564 21565 21566 21567 21568 21569 21570 21571 21572 21573 | return 0; if (OP(preg, p) == BACK) return(p-offset); else return(p+offset); } static int regopsize(regex_t *preg, int p ) { switch (OP(preg, p)) { case REP: case REPMIN: case REPX: case REPXMIN: return 5; case ANYOF: case ANYBUT: case EXACTLY: { int s = p + 2; while (preg->program[s++]) { } return s - p; } } return 2; } size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) { static const char *error_strings[] = { "success", "no match", |
︙ | ︙ | |||
21357 21358 21359 21360 21361 21362 21363 21364 21365 21366 21367 21368 21369 21370 21371 21372 21373 21374 21375 21376 21377 21378 21379 | #endif char *Jim_HistoryGetline(const char *prompt) { #ifdef USE_LINENOISE return linenoise(prompt); #else char *line = malloc(MAX_LINE_LEN); fputs(prompt, stdout); fflush(stdout); if (fgets(line, MAX_LINE_LEN, stdin) == NULL) { free(line); return NULL; } return line; #endif } void Jim_HistoryLoad(const char *filename) { #ifdef USE_LINENOISE | > > > > > | 21738 21739 21740 21741 21742 21743 21744 21745 21746 21747 21748 21749 21750 21751 21752 21753 21754 21755 21756 21757 21758 21759 21760 21761 21762 21763 21764 21765 | #endif char *Jim_HistoryGetline(const char *prompt) { #ifdef USE_LINENOISE return linenoise(prompt); #else int len; char *line = malloc(MAX_LINE_LEN); fputs(prompt, stdout); fflush(stdout); if (fgets(line, MAX_LINE_LEN, stdin) == NULL) { free(line); return NULL; } len = strlen(line); if (len && line[len - 1] == '\n') { line[len - 1] = '\0'; } return line; #endif } void Jim_HistoryLoad(const char *filename) { #ifdef USE_LINENOISE |
︙ | ︙ | |||
21420 21421 21422 21423 21424 21425 21426 | int history_len = strlen(home) + sizeof("/.jim_history"); history_file = Jim_Alloc(history_len); snprintf(history_file, history_len, "%s/.jim_history", home); Jim_HistoryLoad(history_file); } #endif | | | 21806 21807 21808 21809 21810 21811 21812 21813 21814 21815 21816 21817 21818 21819 21820 | int history_len = strlen(home) + sizeof("/.jim_history"); history_file = Jim_Alloc(history_len); snprintf(history_file, history_len, "%s/.jim_history", home); Jim_HistoryLoad(history_file); } #endif printf("Welcome to Jim version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100); Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1"); while (1) { Jim_Obj *scriptObjPtr; const char *result; int reslen; |
︙ | ︙ | |||
21532 21533 21534 21535 21536 21537 21538 21539 21540 21541 21542 21543 21544 21545 21546 21547 21548 21549 21550 21551 21552 21553 21554 21555 | Jim_ListAppendElement(interp, listObj, obj); } Jim_SetVariableStr(interp, "argv", listObj); Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc)); } int main(int argc, char *const argv[]) { int retcode; Jim_Interp *interp; if (argc > 1 && strcmp(argv[1], "--version") == 0) { printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100); return 0; } interp = Jim_CreateInterp(); Jim_RegisterCoreCommands(interp); if (Jim_InitStaticExtensions(interp) != JIM_OK) { | > > > > > > | < | | < | < | 21918 21919 21920 21921 21922 21923 21924 21925 21926 21927 21928 21929 21930 21931 21932 21933 21934 21935 21936 21937 21938 21939 21940 21941 21942 21943 21944 21945 21946 21947 21948 21949 21950 21951 21952 21953 21954 21955 21956 21957 21958 21959 21960 21961 21962 21963 21964 21965 21966 21967 21968 21969 21970 21971 21972 21973 21974 21975 21976 21977 21978 21979 21980 21981 21982 21983 21984 21985 | Jim_ListAppendElement(interp, listObj, obj); } Jim_SetVariableStr(interp, "argv", listObj); Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc)); } static void JimPrintErrorMessage(Jim_Interp *interp) { Jim_MakeErrorMessage(interp); fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); } int main(int argc, char *const argv[]) { int retcode; Jim_Interp *interp; if (argc > 1 && strcmp(argv[1], "--version") == 0) { printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100); return 0; } interp = Jim_CreateInterp(); Jim_RegisterCoreCommands(interp); if (Jim_InitStaticExtensions(interp) != JIM_OK) { JimPrintErrorMessage(interp); } Jim_SetVariableStrWithStr(interp, "jim::argv0", argv[0]); Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0"); retcode = Jim_initjimshInit(interp); if (argc == 1) { if (retcode == JIM_ERR) { JimPrintErrorMessage(interp); } if (retcode != JIM_EXIT) { JimSetArgv(interp, 0, NULL); retcode = Jim_InteractivePrompt(interp); } } else { if (argc > 2 && strcmp(argv[1], "-e") == 0) { JimSetArgv(interp, argc - 3, argv + 3); retcode = Jim_Eval(interp, argv[2]); if (retcode != JIM_ERR) { printf("%s\n", Jim_String(Jim_GetResult(interp))); } } else { Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1)); JimSetArgv(interp, argc - 2, argv + 2); retcode = Jim_EvalFile(interp, argv[1]); } if (retcode == JIM_ERR) { JimPrintErrorMessage(interp); } } if (retcode == JIM_EXIT) { retcode = Jim_GetExitCode(interp); } else if (retcode == JIM_ERR) { retcode = 1; |
︙ | ︙ |
Changes to autosetup/local.tcl.
︙ | ︙ | |||
24 25 26 27 28 29 30 | if {[regexp {^(TCL_[^=]*)=(.*)$} $line -> name value]} { set value [regsub -all {\$\{.*\}} $value ""] set tclconfig($name) [string trim $value '] } } return [array get tclconfig] } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | if {[regexp {^(TCL_[^=]*)=(.*)$} $line -> name value]} { set value [regsub -all {\$\{.*\}} $value ""] set tclconfig($name) [string trim $value '] } } return [array get tclconfig] } # The complex extension checking is done here. global withinfo global extdb # Final determination of module status dict set extdb status {} # Returns 1 if the extension has the attribute proc ext-has {ext attr} { expr {$attr in [dict get $::extdb attrs $ext]} } # Returns an entry from the extension 'info' table, or $default otherwise proc ext-get {ext key {default {}}} { if {[dict exists $::extdb info $ext $key]} { return [dict get $::extdb info $ext $key] } else { return $default } } # Set the status of the extension to the given value, and returns the value proc ext-set-status {ext value} { dict set ::extdb status $ext $value return $value } # Returns the status of the extension, or ? if unknown proc ext-get-status {ext} { if {[dict exists $::extdb status $ext]} { return [dict get $::extdb status $ext] } return ? } proc check-extension-status {ext required} { global withinfo set status [ext-get-status $ext] if {$ext in $withinfo(without)} { # Disabled without further ado msg-result "Extension $ext...disabled" return [ext-set-status $ext n] } if {$status in {m y n}} { return $status } # required is "required" if this extension *must* be enabled # required is "wanted" if it is not fatal for this extension # not to be enabled array set depinfo {m 0 y 0 n 0} # Check direct dependencies if [ext-get $ext check 1] { # "check" conditions are met } else { # not met incr depinfo(n) } if {$depinfo(n) == 0} { # Now extension dependencies foreach i [ext-get $ext dep] { set status [check-extension-status $i $required] #puts "$ext: dep $i $required => $status" incr depinfo($status) if {$depinfo(n)} { break } } } #parray depinfo if {$depinfo(n)} { msg-checking "Extension $ext..." if {$required eq "required"} { user-error "dependencies not met" } msg-result "disabled (dependencies)" return [ext-set-status $ext n] } # Selected as a module? if {$ext in $withinfo(mod)} { if {[ext-has $ext tcl]} { # Easy, a Tcl module msg-result "Extension $ext...tcl" } elseif {[ext-has $ext static]} { user-error "Extension $ext can't be a module" } else { msg-result "Extension $ext...module" foreach i [ext-get $ext libdep] { define-append LDLIBS_$ext [get-define $i ""] } } return [ext-set-status $ext m] } # Selected as a static extension? if {[ext-has $ext shared]} { user-error "Extension $ext can only be selected as a module" } elseif {$ext in $withinfo(ext) || $required eq "$required"} { msg-result "Extension $ext...enabled" } elseif {$ext in $withinfo(maybe)} { msg-result "Extension $ext...enabled (default)" } else { # Could be selected, but isn't (yet) return [ext-set-status $ext x] } foreach i [ext-get $ext libdep] { define-append LDLIBS [get-define $i ""] } return [ext-set-status $ext y] } # Examines the user options (the $withinfo array) # and the extension database ($extdb) to determine # what is selected, and in what way. # # The results are available via ext-get-status # And a dictionary is returned containing four keys: # static-c extensions which are static C # static-tcl extensions which are static Tcl # module-c extensions which are C modules # module-tcl extensions which are Tcl modules proc check-extensions {} { global extdb withinfo # Check valid extension names foreach i [concat $withinfo(ext) $withinfo(mod)] { if {![dict exists $extdb attrs $i]} { user-error "Unknown extension: $i" } } set extlist [lsort [dict keys [dict get $extdb attrs]]] set withinfo(maybe) {} # Now work out the default status. We have. # normal case, include !optional if possible # --without=default, don't include optional if {$withinfo(nodefault)} { lappend withinfo(maybe) stdlib } else { foreach i $extlist { if {![ext-has $i optional]} { lappend withinfo(maybe) $i } } } foreach i $extlist { define LDLIBS_$i "" } foreach i [concat $withinfo(ext) $withinfo(mod)] { check-extension-status $i required } foreach i $withinfo(maybe) { check-extension-status $i wanted } array set extinfo {static-c {} static-tcl {} module-c {} module-tcl {}} foreach i $extlist { set status [ext-get-status $i] set tcl [ext-has $i tcl] switch $status,$tcl { y,1 { define jim_ext_$i lappend extinfo(static-tcl) $i } y,0 { define jim_ext_$i lappend extinfo(static-c) $i # If there are any static C++ extensions, jimsh must be linked using # the C++ compiler if {[ext-has $i cpp]} { define HAVE_CXX_EXTENSIONS } } m,1 { lappend extinfo(module-tcl) $i } m,0 { lappend extinfo(module-c) $i } } } return [array get extinfo] } |
Added fossil.1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | .TH FOSSIL "1" "February 2015" "http://fossil-scm.org" "User Commands" .SH NAME fossil \- Distributed Version Control System .SH SYNOPSIS .B fossil \fIhelp\fR .br .B fossil \fIhelp COMMAND\fR .br .B fossil \fICOMMAND [OPTIONS]\fR .SH DESCRIPTION Fossil is a distributed version control system (DVCS) with built-in wiki, ticket tracker, CGI/http interface, and http server. .SH Common COMMANDs: add clean import pull stash .br addremove clone info purge status .br all commit init push sync .br annotate diff json rebuild tag .br bisect export ls remote-url timeline .br blame extras merge revert ui .br branch finfo mv rm undo .br bundle fusefs open rss unpublish .br cat gdiff praise settings update .br changes help publish sqlite3 version .SH FEATURES Features as described on the fossil home page. .HP 1. .B Integrated Bug Tracking, Wiki, & Technotes - In addition to doing distributed version control like Git and Mercurial, Fossil also supports bug tracking, wiki, and technotes. .HP 2. .B Built-in Web Interface - Fossil has a built-in and intuitive web interface that promotes project situational awareness. Type "fossil ui" and Fossil automatically opens a web browser to a page that shows detailed graphical history and status information on that project. .HP 3. .B Self-Contained - Fossil is a single self-contained stand-alone executable. To install, simply download a precompiled binary for Linux, Mac, OpenBSD, or Windows and put it on your $PATH. Easy-to-compile source code is available for users on other platforms. .HP 4. .B Simple Networking - No custom protocols or TCP ports. Fossil uses plain old HTTP (or HTTPS or SSH) for all network communications, so it works fine from behind restrictive firewalls, including proxies. The protocol is bandwidth efficient to the point that Fossil can be used comfortably over dial-up. .HP 5. .B CGI/SCGI Enabled - No server is required, but if you want to set one up, Fossil supports four simple server configurations. .HP 6. .B Autosync - Fossil supports "autosync" mode which helps to keep projects moving forward by reducing the amount of needless forking and merging often associated with distributed projects. .HP 7. .B Robust & Reliable - Fossil stores content using an enduring file format in an SQLite database so that transactions are atomic even if interrupted by a power loss or system crash. Automatic self-checks verify that all aspects of the repository are consistent prior to each commit. In over seven years of operation, no work has ever been lost after having been committed to a Fossil repository. .SH DOCUMENTATION http://www.fossil-scm.org/ .br .B fossil \fIui\fR |
Deleted fossil.nsi.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added setup/fossil.iss.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | ; ; Copyright (c) 2014 D. Richard Hipp ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the Simplified BSD License (also ; known as the "2-Clause License" or "FreeBSD License".) ; ; This program is distributed in the hope that it will be useful, ; but without any warranty; without even the implied warranty of ; merchantability or fitness for a particular purpose. ; ; Author contact information: ; drh@hwaci.com ; http://www.hwaci.com/drh/ ; [Setup] ArchitecturesAllowed=x86 x64 AlwaysShowComponentsList=false AppCopyright=Copyright (c) D. Richard Hipp. All rights reserved. AppID={{f1c25a1f-3954-4e1a-ac36-4314c52f057c} AppName=Fossil AppPublisher=Fossil Development Team AppPublisherURL=https://www.fossil-scm.org/ AppSupportURL=https://www.fossil-scm.org/ AppUpdatesURL=https://www.fossil-scm.org/ AppVerName=Fossil v{#AppVersion} AppVersion={#AppVersion} AppComments=Simple, high-reliability, distributed software configuration management system. AppReadmeFile=https://www.fossil-scm.org/index.html/doc/tip/www/quickstart.wiki DefaultDirName={pf}\Fossil DefaultGroupName=Fossil OutputBaseFilename=fossil-win32-{#AppVersion} OutputManifestFile=fossil-win32-{#AppVersion}-manifest.txt SetupLogging=true UninstallFilesDir={app}\uninstall VersionInfoVersion={#AppVersion} [Components] Name: Application; Description: Core application.; Types: custom compact full; Flags: fixed [Dirs] Name: {app}\bin [Files] Components: Application; Source: ..\fossil.exe; DestDir: {app}\bin; Flags: restartreplace uninsrestartdelete [Registry] Components: Application; Root: HKLM32; SubKey: Software\Fossil; ValueType: string; ValueName: Install_Dir; ValueData: {app}; Flags: uninsdeletekeyifempty uninsdeletevalue |
Added setup/fossil.nsi.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | ; example2.nsi ; ; This script is based on example1.nsi, but adds uninstall support ; and (optionally) start menu shortcuts. ; ; It will install notepad.exe into a directory that the user selects, ; ; The name of the installer Name "Fossil" ; The file to write OutFile "fossil-setup.exe" ; The default installation directory InstallDir $PROGRAMFILES\Fossil ; Registry key to check for directory (so if you install again, it will ; overwrite the old one automatically) InstallDirRegKey HKLM SOFTWARE\Fossil "Install_Dir" ; The text to prompt the user to enter a directory ComponentText "This will install fossil on your computer." ; The text to prompt the user to enter a directory DirText "Choose a directory to install in to:" ; The stuff to install Section "Fossil (required)" ; Set output path to the installation directory. SetOutPath $INSTDIR ; Put file there File "..\fossil.exe" ; Write the installation path into the registry WriteRegStr HKLM SOFTWARE\Fossil "Install_Dir" "$INSTDIR" ; Write the uninstall keys for Windows WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Fossil" "DisplayName" "Fossil (remove only)" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Fossil" "UninstallString" '"$INSTDIR\uninstall.exe"' WriteUninstaller "uninstall.exe" SectionEnd ; uninstall stuff UninstallText "This will uninstall fossil. Hit next to continue." ; special uninstall section. Section "Uninstall" ; remove registry keys DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Fossil" DeleteRegKey HKLM SOFTWARE\Fossil ; remove files Delete $INSTDIR\fossil.exe ; MUST REMOVE UNINSTALLER, too Delete $INSTDIR\uninstall.exe ; remove shortcuts, if any. RMDir "$SMPROGRAMS\Fossil" RMDir "$INSTDIR" SectionEnd ; eof |
Added skins/README.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | Built-in Skins ============== Each subdirectory under this folder describes a built-in "skin". There are four files in each subdirectory for the CSS, the "details" file, the footer, and the header for that skin. To improve an existing built-in skin, simply edit the appropriate files and recompile. To add a new skin: 1. Create a new subdirectory under skins/. (The new directory is called "skins/newskin" below but you should use a new original name, of course.) 2. Add files skins/newskin/css.txt, skins/newskin/details.txt, skins/newskin/footer.txt and skins/newskin/header.txt. Be sure to "fossil add" these files. 3. Go to the src/ directory and rerun "tclsh makemake.tcl". This step rebuilds the various makefiles so that they have dependencies on the skin files you just installed. 4. Edit the BuiltinSkin[] array near the top of the src/skins.c source file so that it describes and references the "newskin" skin. 5. Type "make" to rebuild. Development Hints ----------------- One way to develop a new skin is to copy the baseline files (css.txt, details.txt, footer.txt, and header.txt) into a working directory $WORKDIR then launch Fossil with a command-line option "--skin $WORKDIR". Example: cp -r skins/default newskin fossil ui --skin ./newskin When the argument to --skin contains one or more '/' characters, the appropriate skin files are read from disk from the directory specified. So after launching fossil as shown above, you can edit the newskin/css.txt, newskin/details.txt, newskin/footer.txt, and newskin/header.txt files using your favorite text editor, then press Reload on your browser to see immediate results. |
Added skins/aht/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/black_and_white/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | /* General settings for the entire page */ body { margin:0px 0px 0px 0px; padding:0px; font-family:verdana, arial, helvetica, "sans serif"; color:#333; background-color:white; -moz-text-size-adjust: none; -webkit-text-size-adjust: none; -mx-text-size-adjust: none; } /* consistent colours */ h2 { color: #333; } h3 { color: #333; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: table-cell; text-align: left; vertical-align: bottom; font-weight: bold; color: #333; white-space: nowrap; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 2em; font-weight: bold; text-align: center; color: #333; vertical-align: bottom; width: 100%; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; padding-right: 10px; text-align: right; vertical-align: bottom; padding-bottom: 5px; color: #333; font-size: 0.8em; font-weight: bold; white-space: nowrap; } /* The header across the top of the page */ div.header { margin:10px 0px 10px 0px; padding:1px 0px 0px 20px; border-style:solid; border-color:black; border-width:1px 0px; background-color:#eee; } /* The main menu bar that appears at the top left of the page beneath ** the header. Width must be co-ordinated with the container below */ div.mainmenu { float: left; margin-left: 10px; margin-right: 10px; font-size: 0.9em; font-weight: bold; padding:5px; background-color:#eee; border:1px solid #999; width:8em; } /* Main menu is now a list */ div.mainmenu ul { padding: 0; list-style:none; } div.mainmenu a, div.mainmenu a:visited{ padding: 1px 10px 1px 10px; color: #333; text-decoration: none; } div.mainmenu a:hover { color: #eee; background-color: #333; } /* Container for the sub-menu and content so they don't spread ** out underneath the main menu */ #container { padding-left: 9em; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu, div.sectionmenu { padding: 3px 10px 3px 10px; font-size: 0.9em; text-align: center; border:1px solid #999; border-width:1px 0px; background-color: #eee; color: #333; } div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { padding: 3px 10px 3px 10px; color: #333; text-decoration: none; } div.submenu a:hover, div.sectionmenu>a.button:hover { color: #eee; background-color: #333; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { padding: 2ex 1ex 0ex 2ex; } /* Some pages have section dividers */ div.section { margin-bottom: 0px; margin-top: 1em; padding: 1px 1px 1px 1px; font-size: 1.2em; font-weight: bold; border-style:solid; border-color:#999; border-width:1px 0px; background-color: #eee; color: #333; white-space: nowrap; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #eee; border: 2px #999 solid; font-size: 1em; font-weight: normal; padding: .25em; margin: .2em 0 .2em 0; float: left; clear: left; color: #333; white-space: nowrap; } /* The footer at the very bottom of the page */ div.footer { font-size: 0.8em; margin-top: 12px; padding: 5px 10px 5px 10px; text-align: right; background-color: #eee; color: #555; } /* <verbatim> blocks */ pre.verbatim { background-color: #f5f5f5; padding: 0.5em; white-space: pre-wrap; } /* The label/value pairs on (for example) the ci page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } |
Added skins/black_and_white/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/black_and_white/footer.txt.
> > > > | 1 2 3 4 | <div class="footer"> Fossil version $manifest_version $manifest_date </div> </body></html> |
Added skins/black_and_white/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss"> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen"> </head> <body> <div class="header"> <div class="logo"> <img src="$logo_image_url" alt="logo"> <br />$<project_name> </div> <div class="title">$<title></div> <div class="status"><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></div> </div> <div class="mainmenu"> <th1> html "<a href='$home$index_page'>Home</a>\n" if {[anycap jor]} { html "<a href='$home/timeline'>Timeline</a>\n" } if {[anoncap oh]} { html "<a href='$home/tree?ci=tip'>Files</a>\n" } if {[anoncap o]} { html "<a href='$home/brlist'>Branches</a>\n" html "<a href='$home/taglist'>Tags</a>\n" } if {[anoncap r]} { html "<a href='$home/ticket'>Tickets</a>\n" } if {[anoncap j]} { html "<a href='$home/wiki'>Wiki</a>\n" } if {[hascap s]} { html "<a href='$home/setup'>Admin</a>\n" } elseif {[hascap a]} { html "<a href='$home/setup_ulist'>Users</a>\n" } if {[info exists login]} { html "<a href='$home/login'>Logout</a>\n" } else { html "<a href='$home/login'>Login</a>\n" } </th1></ul></div> |
Added skins/blitz/README.md.
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | ## Blitz Theme Contributed by James Moger (james.moger@gitblit.com) This theme is inspired by my own project, [Gitblit](http://gitblit.com), and offered to the Fossil project. This theme embeds & uses an unmodified 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). This theme uses half of a heavily-modified version of [Skeleton](http://getskeleton.com) which is distributed under an [MIT license](https://github.com/dhg/Skeleton/blob/master/LICENSE.md). None of the responsive elements (media queries) are included at this time. The font used in the included Fossil logo image is [Trillium Web Light](http://www.google.com/fonts/specimen/Titillium+Web) @ 48px HTML color code #456a7a. The RSS feed icon is sourced from [Font-Awesome](https://fortawesome.github.io/Font-Awesome/icons) by Dave Gandy and is distributed under the [SIL OFL 1.1 ](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) license. |
Added skins/blitz/arrow_project.png.
cannot compute difference between binary files
Added skins/blitz/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ /** * 1. Set default font family to sans-serif. * 2. Prevent iOS text size adjust after orientation change, without disabling * user zoom. */ html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ } /** * Remove default margin. */ body { margin: 0; } /* HTML5 display definitions ========================================================================== */ /** * Correct `block` display not defined for any HTML5 element in IE 8/9. * Correct `block` display not defined for `details` or `summary` in IE 10/11 * and Firefox. * Correct `block` display not defined for `main` in IE 11. */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } /** * 1. Correct `inline-block` display not defined in IE 8/9. * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. */ audio, canvas, progress, video { display: inline-block; /* 1 */ vertical-align: baseline; /* 2 */ } /** * Prevent modern browsers from displaying `audio` without controls. * Remove excess height in iOS 5 devices. */ audio:not([controls]) { display: none; height: 0; } /** * Address `[hidden]` styling not present in IE 8/9/10. * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. */ [hidden], template { display: none; } /* Links ========================================================================== */ /** * Remove the gray background color from active links in IE 10. */ a { background-color: transparent; } /** * Improve readability when focused and also mouse hovered in all browsers. */ a:active, a:hover { outline: 0; } /* Text-level semantics ========================================================================== */ /** * Address styling not present in IE 8/9/10/11, Safari, and Chrome. */ abbr[title] { border-bottom: 1px dotted; } /** * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. */ b, strong { font-weight: bold; } /** * Address styling not present in Safari and Chrome. */ dfn { font-style: italic; } /** * Address variable `h1` font-size and margin within `section` and `article` * contexts in Firefox 4+, Safari, and Chrome. */ h1 { font-size: 2em; margin: 0.67em 0; } /** * Address styling not present in IE 8/9. */ mark { background: #ff0; color: #000; } /** * Address inconsistent and variable font size in all browsers. */ small { font-size: 80%; } /** * Prevent `sub` and `sup` affecting `line-height` in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } /* Embedded content ========================================================================== */ /** * Remove border when inside `a` element in IE 8/9/10. */ img { border: 0; } /** * Correct overflow not hidden in IE 9/10/11. */ svg:not(:root) { overflow: hidden; } /* Grouping content ========================================================================== */ /** * Address margin not present in IE 8/9 and Safari. */ figure { margin: 1em 40px; } /** * Address differences between Firefox and other browsers. */ hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; } /** * Contain overflow in all browsers. */ pre { overflow: auto; } /** * Address odd `em`-unit font size rendering in all browsers. */ code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } /* Forms ========================================================================== */ /** * Known limitation: by default, Chrome and Safari on OS X allow very limited * styling of `select`, unless a `border` property is set. */ /** * 1. Correct color not being inherited. * Known issue: affects color of disabled elements. * 2. Correct font properties not being inherited. * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. */ button, input, optgroup, select, textarea { color: inherit; /* 1 */ font: inherit; /* 2 */ margin: 0; /* 3 */ } /** * Address `overflow` set to `hidden` in IE 8/9/10/11. */ button { overflow: visible; } /** * Address inconsistent `text-transform` inheritance for `button` and `select`. * All other form control elements do not inherit `text-transform` values. * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. * Correct `select` style inheritance in Firefox. */ button, select { text-transform: none; } /** * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` * and `video` controls. * 2. Correct inability to style clickable `input` types in iOS. * 3. Improve usability and consistency of cursor style between image-type * `input` and others. */ button, html input[type="button"], /* 1 */ input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ } /** * Re-set default cursor for disabled elements. */ button[disabled], html input[disabled] { cursor: default; } /** * Remove inner padding and border in Firefox 4+. */ button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } /** * Address Firefox 4+ setting `line-height` on `input` using `!important` in * the UA stylesheet. */ input { line-height: normal; } /** * It's recommended that you don't attempt to style these elements. * Firefox's implementation doesn't respect box-sizing, padding, or width. * * 1. Address box sizing set to `content-box` in IE 8/9/10. * 2. Remove excess padding in IE 8/9/10. */ input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } /** * Fix the cursor style for Chrome's increment/decrement buttons. For certain * `font-size` values of the `input`, it causes the cursor style of the * decrement button to change from `default` to `text`. */ input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } /** * 1. Address `appearance` set to `searchfield` in Safari and Chrome. * 2. Address `box-sizing` set to `border-box` in Safari and Chrome * (include `-moz` to future-proof). */ input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; } /** * Remove inner padding and search cancel button in Safari and Chrome on OS X. * Safari (but not Chrome) clips the cancel button when the search input has * padding (and `textfield` appearance). */ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } /** * Define consistent border, margin, and padding. */ fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } /** * 1. Correct `color` not being inherited in IE 8/9/10/11. * 2. Remove padding so people aren't caught out if they zero out fieldsets. */ legend { border: 0; /* 1 */ padding: 0; /* 2 */ } /** * Remove default vertical scrollbar in IE 8/9/10/11. */ textarea { overflow: auto; } /** * Don't inherit the `font-weight` (applied by a rule above). * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. */ optgroup { font-weight: bold; } /* Tables ========================================================================== */ /** * Remove most spacing between table cells. */ table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; } /* * Blitz * * Skin inspired by Gitblit with heavily-modified excerpts from Skeleton 2.0.4. * Blitz is authored by james.moger@gitblit.com. * * Skeleton is authored by Dave Gamache and is distributed under the MIT license. * http://getskeleton.com * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ html { /* 62.5% so that the REM values are base 10px. */ /* 1.5rem = 15px */ font-size: 62.5%; } /* Typography ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ h1, h2, h3, h4, h5, h6 { margin: 0; margin-bottom: 1rem; font-weight: 700; } h1 { font-size: 3.0rem; line-height: 1.2; } h2 { font-size: 2.6rem; line-height: 1.25; } h3 { font-size: 2.4rem; line-height: 1.3; } h4 { font-size: 2.0rem; line-height: 1.35; } h5 { font-size: 1.6rem; line-height: 1.5; } h6 { font-size: 1.4rem; line-height: 1.6; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { font-size: 0.75em; font-weight: 400; color: #ccc; } pre, code { font-size: 1.2rem; } body { font-size: 1.4em; /* currently ems cause chrome bug misinterpreting rems on body element */ line-height: 1.5; font-weight: 400; font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; color: #333; background-color: #f8f8f8; } /* Spacing ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ button, .button { margin-bottom: 1rem; } input, textarea, select, fieldset, pre, blockquote, dl, figure, table, p, ul, ol { margin-bottom: 1rem; } p { margin-top: 0; } hr { margin-top: 3rem; margin-bottom: 3.5rem; border-width: 0; border-top: 1px solid #ccc; } /* Buttons ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .button, button, input[type="button"], input[type="reset"], input[type="submit"] { display: inline-block; height: 3.3rem; padding: 0 2.2rem; color: #555 !important; text-align: center; font-size: 1.1rem; font-weight: 700; line-height: 3.3rem; letter-spacing: .08rem; text-transform: uppercase; text-decoration: none; white-space: nowrap; background-color: transparent; border-radius: 4px; border: 1px solid #ccc; cursor: pointer; box-sizing: border-box; } .button:hover, button:hover, input[type="button"]:hover, input[type="reset"]:hover, .button:focus, button:focus, input[type="button"]:focus, input[type="reset"]:focus { color: #444 !important; background-color: #eee; border-color: #aaa; outline: 0; } input[type="submit"] { color: white !important; background-color: #446979; border-color: #446979; } input[type="submit"]:hover, input[type="submit"]:focus { color: white !important; background-color: #648898; border-color: #648898; } /* Forms ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ input[type="email"], input[type="number"], input[type="search"], input[type="text"], input[type="tel"], input[type="url"], input[type="password"], textarea, select { height: 3.3rem; padding: 6px 10px; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; box-shadow: none; box-sizing: border-box; } /* Removes awkward default styles on some inputs for iOS */ input[type="email"], input[type="number"], input[type="search"], input[type="text"], input[type="tel"], input[type="url"], input[type="password"], textarea { -webkit-appearance: none; -moz-appearance: none; appearance: none; } textarea { height: inherit; min-height: 65px; padding-top: 6px; padding-bottom: 6px; } input[type="email"]:focus, input[type="number"]:focus, input[type="search"]:focus, input[type="text"]:focus, input[type="tel"]:focus, input[type="url"]:focus, input[type="password"]:focus, textarea:focus, select:focus { border: 1px solid #aaa; outline: 0; } label, legend { display: block; margin-bottom: .5rem; font-weight: 700; } fieldset { padding: 0; border-width: 0; } input[type="checkbox"], input[type="radio"] { display: inline; } /* Links ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ a { color: #446979; text-decoration: none; } a:hover { text-decoration: underline; } /* Lists ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ ul { list-style: square; } ol { list-style: decimal; } ol, ul { padding-left: 3rem; margin-top: 0; } li { margin-bottom: 0.5rem; } /* Nested Lists ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ ul ul, ul ol, ol ol, ol ul { margin: 1rem 0 1rem 2rem; } /* Code ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ code, kbd { padding: .2rem .5rem; margin: 0 .2rem; white-space: nowrap; background: #f8f8f8; border: 1px solid #ccc; border-radius: 4px; } pre > code { display: block; padding: 1rem 1.5rem; white-space: pre; } pre.verbatim { background-color: inherit; white-space: pre-wrap; } /* Blockquote ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ blockquote { padding: 0px 20px; margin: 0 0 20px; border-left: 4px solid #ccc; } /* Tables ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ th, td { padding: 6px 5px; text-align: left; border-bottom: 1px solid #ddd; } th:first-child, td:first-child { padding-left: 0; } th:last-child, td:last-child { padding-right: 0; } /* * Blitz Page Layout Design * * html > body > header > container > mainmenu * middle > container > submenu & content * footer > container > generation stats, fossil logo, version * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ /* Container * Represents the usable layout space for header, middle, and footer. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .container { position: relative; width: 100%; max-width: 900px; margin: 0 auto; box-sizing: border-box; } /* Header * Div displayed at the top of every page. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .header { color: #666; font-weight: 400; padding-top: 10px; border-width: 0px; border-top: 4px solid #446979; border-bottom: 1px solid #ccc; } .header .logo { display: inline-block; } .header .login { padding-top: 2px; text-align: right; } .header .login .button { margin: 0; } .header h1 { margin: 0px; color: #666; display: inline-block; } .header .logo h1 { display: inline-block; } .header .title h1 { padding-bottom: 10px; } .header h1 small, .header h2 small { color: #888; } .header a.rss { display: inline-block; padding: 10px 15px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMNDhwn05VjawAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAGlSURBVDjLrdPfb8xREAXwT7tIl+paVNaPJghCKC8kXv0XXvyNXsRfwYPQJqVKiqykWFVZXd12vcxNJtduUtJJvrm7984998ycMxxwNGI9jPs4j7nY+/U/gIdiPYO71dk21rCE7r8ybOHGmMfmcRNnsbEf1gXwNzqYSXs5WljEMXzAaBLg1Ji9Js7hOi6OeeAznqC/X8AcMyHWYpX7E4/Rm1QyHMdefCWGeI/VcMDR2D8S7Fci5y/AeTzCPVyLi1sYJAut4BTaiX0n9kc14MmkcjPY3I5LXezGtxqKtyJ3Lir6VAM2AmCq6m8Hl6PsQTB5hyvxmMhZxk4G3MZLfAwLtdNZM9rwOs528TVVNB3ga7UoQ2wGmyWciFaU0VwIJiP8iL6Xfp7GK+w0JthliDep8UKonTSGvbBTaU8f3QzYxgPcCsBvWK9E6OBFCNGPVjTTqC430p+H6fLVGLGtmIw7SbwevqT+XkgVPJ9Otpmtyl6I9XswLXEp/d6oPN0ugJu14xMLob4kgPRYjtkCOMDTUG+AZ3ibEtfDLorfEmAB3UuTdXDxBzUUZV+B82aLAAAAAElFTkSuQmCC); background-position: center center; background-repeat: no-repeat; } /* Middle * Center div displayed between header and footer. Contains per-page content. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .middle { background-color: white; padding-bottom: 20px; max-width: 100%; box-sizing: border-box; } /* Content * Displayed below submenu within the middle div. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .content { padding-top: 15px; } .content a { color: #002060; } /* Footer * Displayed after the middle div and forms the page bottom. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .footer { padding: 10px 0 60px; border-top: 1px solid #ccc; background-color: #f8f8f8; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABGCAQAAADxl9ugAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffAwkQBRPw+yfrAAAAPHRFWHRDb21tZW50ACBJbWFnZSBnZW5lcmF0ZWQgYnkgRVNQIEdob3N0c2NyaXB0IChkZXZpY2U9cG5tcmF3KQqV01S1AAANg0lEQVRo3s2aa3RVRZbH/3tXnftIbhJCeJMXyEsNCsgrSBzAYXyt0YVj2zSOonbLqNNO+25FB2kdoBVdrp6l09P2NNpOL/ExSrtUdGRU5JEQgggCggIhQIBAIIGQ5N57TlXt+ZAICY+AGCT76zlV91dVe+/a+38uDQ7dYmrxw41QocJuni28vWTedEa7Gd9iatplos7Y2mfDRED6A2G0I2At6AdPIiA84HgbLhx1o9QX9Zoh7QjYHpOkYJYrfNyOKH0O/f2FJpfkSRmMCNqDk9rDXwTAuxK7h3uFf7vXxC6QVO8w19taNOT4Y0wu1sCdW0AA6IZ73bCUaI/GHuqnsJqcEqVrzSo+wNtLdgAPsz6jeVURtQ9gA8bQT8xztW7X2u3I4G6UdA0cohxcSAN7nt939/zDQzl0LnfwaLpxqFT/i7w7XLYXhoUQ+YFW3pJlC0/nt6jZZb4zjXY2AaGXvQOz/gMY09NM4C6ixRL7OU3PTm4ORdiIjyLsJvq1Z28Hj5rBM82xMTqPuhHznmU72vq12W5M3+LykQWUW9pirzXOmmk8wjE87kb8HPmSKlY+wY62R9jhI69xf/AmYiEfiXs+e4CCAZiDUc9wP6cRIJn8tq23PQClbwap9Ivk7usiGj/CDs5xl3Qe/EiQCEWsgXLlq8sfamM73osMna43ef/pfm731Hsxv+Z00ozFzaJpF+gMrsNB2BwL3cGpSsNRWHbmvTQNiTbev9q4xXszMLzqpdgDNjxv/YjmvHlSQIc5brl8nrqT5pllMpHM9wRc4gU3oC8JyDLt8P78ZLyM2lrmAeTg5YrKdf1S1N9QTs6At1YM49DJo1gwWy7uE5kKBQHbler9K8R+L8API2qmFoDY7ejxpxGJxtMctyZcfYvJDYX4i5LXp/MJAS2edkM6edNUDuLNOdMDmz913bIw8RidKO/1w+3NQfePbHQjdbd/NKOjbq7UqQOmYtXb/+BdbGc4AJjPX53CXe6UGweYWxUR8N8rNkzn4wAFc9yYPvGbI+mttoycpo14fWbD0hZv+njOAcBl6Yl8jiEMbSKhqGPypZ5N0B2kDxrjOjF0QuI2oRqj2z6rAebx5pNiRrEo0jhV9wFkP//xpkPHAU6UGdnBLyR8jMOQs6xwcMAznVys+Uqa5YCCnilFkuVSkeKlGKfDED8JaI+VifMWR9LPC/u+UkoZA6edOezVu83pny4yD6qQnCxhDzk/MhUED+8PWHJMkAgWROvvQoxOcEdayynVY9/5tIgAwsfIvaD3XaFx6M6ddMx3WlurCGICiAIhMLxB1VM+wQRMDOeYjHCUMum85OW909/acOlJgqaI3qrulU9dYGlgzVI1tsVrETzlOt3MeUGCudVoCgJmAhFU78F+6Usu0V39iylSHCRZMROM0do5pUDWQamEPZTy5oqyXVt77bG94HGEyTkCE8BsjLOcl11Uvf7V+qvIP86jgWH8zhc9LpKYU7qRhH5NTRknwFw3+jpMQCIItG69PCLfZ1YKAMlS/7PQDCc2qbRzSjET4slINBAvhsPxjarKKy/9FhjZG8Ol4tINi/uFzkcfl8sEnwExATGRY73Ze2VpAHxHoPFki7r2iljN3a6LqqOxnZ+vXUBNwTF6uL0e0Nr3tSYWpqNhQtaIaA0A1oZftuf5Y1xmCMRO2IM1VXqf3aYr5IDXODTx7+43PAjPFcit1MANeHfFxuFpSFW9TbbORpaNEqt6/5tIoxuBOO049NHXNTN5lFwlE3o09JUMlWKdSmJ/UC8TvRwqjNzoVwNIw3upcluyq4hSAq3Idyt5HOyRIDGuGdA42un+6wv/Cq7sFA1F/B4N/5MEgL/yOsSPlFSMOzD5JhkMEQ8l8u41Job7hVoExsiRNMU1Biac4oqD92Np8Xt0BgUiEOeIHLyobbQb9U/8fSAItsFdJv1wECASB5L92xb2yefcE3Q+jgsof//m3+PO2i7YhgV0HisAK48UnE03EaT67axMyjMNXpFkf/za8r3VPP2I46Ri+aYDW0z3SMT6NJb62wPiJEFhEoFCYIhNoyS4VF1CBKArPu6MKWysBZhFVFrD7yri2VkuR4QZAIkTMAOAiDIY9KvFQ3gVPqXVFD9JSbSS3vP7b/QzKY8SlO6G5e2fv/dozvDxciKvCqOYrIURT/XU2+0SKrdVqtpVojz4Sq1Vi1duV0UE+PSEy33AhRRZAzDDc4vXrB1xKU0gRUR0LCCRi/be8XZ12/0M41Ka5K/5JjA80PlQKMjfeVVN9ZG0NZbmH8xf5Y8nJxIKmYC7SmLlexeVp2wKb+y++f6KZ3c/03g+qSISPONGDqHRZOk7QGde6XsrjTP+kXTTCpCVGErZteZUDRdhD3Ls65tz64J85ZGyQ0tXDAzckadFlOtv+ZQLoYMgHHaGcrJ7fb56inR3WVJJRVTdVM3kQSh5r1hjlbIGIKVX6ammE1vnmImh4Y4BZHGcvmvx5XQ6BcRIfnPHxV82ZnFUYsh+bWXLzMuwquGwCnvdBUTkqHcPmb+l5cJZcKervlwc8N04sbYwSJC1tsnneemJqh32LksZflr1SUh+pT6q+/ov/krztitDq1s4hP1p7mbfuFfkEKJgSejLh/Wa7Y5GJsdQmEYDoVocDCvnLCvnwPDMzOK/uiVoPS8IxoQi409D2xAU4Hd2xE2DZ3gjvcpVZY+0iqgAbFh7F9grp84xf5D6QFOE/rYo1vto4z6ZVg/COHKAiGJrACaljNHaiaqTp0NeXlr37Ydz/ahSLY9YzLIPMzlxSrxMzM/ochfnO8UHvJJpgTnm+UE5GKMeOu3LrLJPd32+Z1FamfczKli36jzTtHplvPg46o2WgNwEaOqoTk8Jxsu4+vGUruDkCCCJkg171g2ntqMkjrmuYUBwG7LglIq/U7Zr1HEjJrtiay9imIzsqt37gb4/RSZlSsP88iZf5T3kX2xOuBEck2yp13F32NbDuZbHqSjwFrzJbeP5eN6NHOJuoQyXRIZ7Y+3Gh08wohjpW/VOkFMyqagHILnOSAOuflh1bqKggew524Zs1ex+R6cmz1W7Z6clvzzF8T7rLsvnSfBgJVO9cN+ax4/IRwazZLZrqmOSWJjAR0hCdFc/F2AtToTos34POQHAkVw2Sp2m7EKwOEyLVj59w6EtpyjdFYSCCYhaC0n9fcmmr5pb8QBrQl0wqn/hNc82J8Rfc8lWVwVGwLeNDVPTdW30+KZN0Sab5JTBqOBBowIHzeZVJcBjXHdKXfQp93+DgwuIgy9QtqJ8evPhOtRRzZi30iKfBZPxQVNhofDP6sUXh05zA5MH+WKdpAgAJ/lAChqhyQmd5HgFChFoMvar0AaKq8qSGmA6o03lVFCEqx1Q0Ce0075hkgvW93EPNeMZ3Cl93eRl5Q8GVjoBs1tUN1l/rvkn1VOuNXUIAyATv6zz3bXPEo3t7N/L6bBirfV0MimktOclgnDUJHiTWxo9oJOZQT/TDUk0nIYfFODFaMMN4WFqbnEl8BzXtlBX++ATr2KW3eC9kbg/9d2l6/qmdxrq9bdRkBwMtrgd+lpkpxwyWSwAkvRKSfl0phf4l+6acGVeak6QQbCJYH/Gwci+zPi/+oMFEJpLdTDfQ8IpTTl8r/lkQuniX3oR98mjX65sJQZ0wYNu1NU0wZ8RjSUf0Qo22aCU1oAoCazTFe5bdZ1pVIqcv2B12XSm6awxCFNaichCr1I1auDOQFvKxzQHFD4mexGXNfds/PoYB+iMhxzwIC8vcJe7HKVIrBEoLRQkuVL+Ii70lDlMUOR/uHrJdNaAwXo82orksR8gfVUAGFkgy5JfhKdEKncelwhq8SinwMeidX+303VHuvOgCGSRRF2o6vb6FyKmQTGRNTraQt1qJ6EaAPA4V22MYH20wXx+aCyfKFvFAcymiYfCh/YhSQZACFHpBB/l6On2l0s/AgTBWdIHHbpIulzfWDbvAW4r3gPUIwWZ0lW6SoaE0AgDYG6Six0R4OjgWRMwGUAVHuUz+SS2iNQ+XSMAvKqzrLCemdtUo3ifWhpoTi7b/RSfVRH9zBc2y43umXVgtImf3R08c7zCIeYpVoPOtoh+ZjYEhd3kWtV4IHSldEjAV7U/ATHxig89Tx0QUFCtvUJisxnNknuHArSY4+xtNuk4ZQWai4wOBThJRqZJvhYXtptnNTVAHQnQYbT4k7QI6/Vd6w6f/U9h39cUgEg+BCG3rk/AHQ9wlhsz1IUCI3tRPrDVtdlhzO9rxPNke+nuXR0TUIdAMOrDlkJshwEM4QId98C8vKTmUUJHAwww03VNT8ny9hd/cJ9qCaU7QnrpjXvcsKuT6W7VdcvHc+sq95wDCuZILxp9t+TZJWVL/56PPdJzDhjDhG75UyUrsXXtBw9z/PjAObd3729leA++mTOldO07dyl9ghbmnALmYpjybndhO3/VV/dx9IQdlj53YQEAo2din3p1cv1VfDIF8EfvSdJQSZvU3ljGJJOvNuH94kOPs22jwWq3P5edTryG8G/OdtsxOPiZd6O1ppTeK0n43Hb/9yPuYBhPuFFXumEq3231S+ibL/e+yhtP2Zz+iD74hCu83vblr+W1yJ5LgguxmzedRu/8//AUSaqMTR1xAAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: center top 10px; } .footer a { color: #3b5c6b; } /* Main Menu * Displayed in header, contains repository links. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .mainmenu { clear:both; } .mainmenu ul { list-style: none outside; display: block; position: relative; border-top: 1px solid #ccc; padding: 0; } .mainmenu li { outline: 0; display: block; float: left; margin: 0; } .mainmenu li.active { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB90FDxEXAZ2XRzAAAABJSURBVCjPY2CgBzhz5sx/QmoYiTXAxMSEkWRDsLkAl0GMpHoBm0EoAlu3bmUQFxcnGAboBjEhc4gxAJtLGUmJBVwuYiTXAGSDAIx5IBObnuVxAAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: center bottom; } .mainmenu li a { color: #3b5c6b; display: block; padding: 10px 15px; } .mainmenu li.active a { font-weight: bold; } .mainmenu li:hover { background-color: #eee; } /* Submenu * Displayed in the middle div. Contains page-specific form controls. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .submenu { padding: 10px 0px; border-bottom: 1px solid #ddd; } .submenu input, .submenu select { margin: 0 0 0 5px; } .submenu a { color: #3b5c6b; padding: 5px 15px; text-decoration: none; border: 1px solid transparent; border-radius: 5px; } .submenu a:hover { border: 1px solid #ccc; } /* Section * Cap/header to distinguish a section. Displayed within a content div. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .section { font-weight: bold; background-color: #f5f5f5; border: 1px solid #ccc; padding: 9px 10px 10px; margin: 10px 0; } /* Section Menu * Div of buttons/links, displayed with a section div within a content div. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .sectionmenu { border: 1px solid #ccc; border-top: 0; margin-top: -10px; margin-bottom: 10px; padding: 5px; text-align: center; } .sectionmenu a { display: inline-block; margin-top: 5px; margin-right: 1em; } /* File browser * Repository tree navigation. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ ul.browser { list-style: none; } ul.browser li.dir { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExABnLjGZQAAAEFJREFUOMtjYKAQMIaGhv4npGj16tWMuORYGBgYGOZW+eDUnNy2Ba/hLMQ4E58rCRpAyHVMlAbiqAGjBhCdmWgKAHp4Dh0ZusP3AAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: 0px center; padding-left: 22px; padding-top: 2px; } ul.browser li.file { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExMaPfBcSgAAAFNJREFUOMvtkzEOwDAIA02VL5pHwiOTJZFQmkqFOTex+CwPCCYkAaDjB+4u65ZdYGafQVV9SR4kWQUke0mwS1o2HGcAQKs0R1lpQuQKruD4jVnBAG/cGRqf0U66AAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: 0px center; padding-left: 22px; padding-top: 2px; } div.filetreeline { display: table; width: 100%; white-space: nowrap; } /* tree-view top-level list */ .filetree > ul { display: inline-block; } /* tree-view lists */ .filetree ul { margin: 0; padding: 0; list-style: none; } /* tree-view collapsed list */ .filetree ul.collapsed { display: none; } /* tree-view lists below the root */ .filetree ul ul { position: relative; margin: 0 0 0 21px; } /* tree-view lists items */ .filetree li { position: relative; margin: 0; padding: 0; } /* tree-view node lines */ .filetree li li:before { content: ''; position: absolute; top: -.8em; left: -14px; width: 16px; height: 1.5em; border-left: 1px solid #ccc; border-bottom: 1px solid #ccc; } /* tree-view directory lines */ .filetree li > ul:before { content: ''; position: absolute; top: -1.5em; bottom: 0; left: -35px; border-left: 1px solid #ccc; } /* hide lines for last-child directories */ .filetree li.last > ul:before { display: none; } .filetree a { position: relative; z-index: 1; display: table-cell; min-height: 16px; padding-left: 22px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExMaPfBcSgAAAFNJREFUOMvtkzEOwDAIA02VL5pHwiOTJZFQmkqFOTex+CwPCCYkAaDjB+4u65ZdYGafQVV9SR4kWQUke0mwS1o2HGcAQKs0R1lpQuQKruD4jVnBAG/cGRqf0U66AAAAAElFTkSuQmCC); background-position: center left; background-repeat: no-repeat; } .filetree .dir > div.filetreeline > a { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExABnLjGZQAAAEFJREFUOMtjYKAQMIaGhv4npGj16tWMuORYGBgYGOZW+eDUnNy2Ba/hLMQ4E58rCRpAyHVMlAbiqAGjBhCdmWgKAHp4Dh0ZusP3AAAAAElFTkSuQmCC); } /* Label-Value table * Displayed on the Check-in & Admin pages. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.label-value th { vertical-align: middle; } /* Branches table ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .brlist table td { padding: 5px; } /* Timeline * Displays chronologically-ordered check-ins with a branch graph. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ tr.timelineCurrent { border-left: 2px solid orange; background-color: #ffc; } tr.timelineSelected { border-left: 2px solid orange; background-color: #ffffe8; } tr.timelineCurrent td.timelineTableCell { } tr.timelineSpacer { } div.timelineDate { font-weight: bold; white-space: nowrap; } a.timelineHistLink { text-transform: lowercase; } span.timelineComment { padding: 0px 5px; } /* Login/Loguot ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.login_out { } table.login_out .login_out_label { font-weight: 700; text-align: right; } table.login_out td { border: 0; } /* Diff displays ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ pre.udiff, table.sbsdiffcols { width: 100%; overflow: auto; border: 1px solid #ccc; padding: 5px; font-size: 1rem; } pre.udiff:focus, table.sbsdiffcols:focus { outline: none; } /* Ticket Reports ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.report { width: 100%; cursor: auto; border-radius: 4px; border: 1px solid #ccc; margin: 0 0 1em 0; } .report td, .report th { border: 0; font-size: .9em; padding: 5px; } .report th { cursor: pointer; } .report thead+tbody tr:hover { background-color: #f5f9fc !important; } /* Ticket page ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.tktDsp { border-top: 1px solid #ccc; border-left: 1px solid #ccc; width: 100%; margin: 15px 0px 10px 0px; } td.tktDspLabel, td.tktDescLabel { width: 70px; text-align: right; overflow: hidden; font-weight: 700; padding: 10px; background-color: #f8f8f8; } td.tktDescLabel { vertical-align: top; } td.tktDspValue, td.tktDescValue { text-align: left; vertical-align: top; border: 1px solid #ccc; padding: 10px; } td.tktDspValue pre, td.tktDescValue pre, td.tktDspValue code, td.tktDescValue code { white-space: pre-wrap; } div.tktComments { width: 100%; margin: 30px 0px 10px 0px; } div.tktComment { } div.tktCommentHeader { border: 1px solid #ccc; background-color: #f8f8f8; padding: 10px 10px; margin-bottom: 10px; } span.tktCommentLogin { display: inline-block; font-weight: 700; color: #002060; } div.tktCommentBody { margin: 10px 40px 30px; } /* User setup table ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ td.usetupEditLabel { font-weight: 700; } /* Utilities ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .full-width { width: 100%; box-sizing: border-box; } .max-full-width { max-width: 100%; box-sizing: border-box; } .pull-right { float: right; } .pull-left { float: left; } /* Clearing ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .container:after, .mainmenu:after, .row:after, .u-cf { content: ""; display: table; clear: both; } |
Added skins/blitz/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 0 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 |
Added skins/blitz/dir.png.
cannot compute difference between binary files
Added skins/blitz/file.png.
cannot compute difference between binary files
Added skins/blitz/footer.txt.
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | </div> <!-- end div container --> </div> <!-- end div middle max-full-width --> <div class="footer"> <div class="container"> <div class="pull-right"> <a href="http://fossil-scm.org">Fossil version $manifest_version $manifest_date</a> </div> This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s </div> </div> </body> </html> |
Added skins/blitz/fossil_100.png.
cannot compute difference between binary files
Added skins/blitz/fossil_80_reversed_darkcyan.png.
cannot compute difference between binary files
Added skins/blitz/fossil_80_reversed_darkcyan_text.png.
cannot compute difference between binary files
Added skins/blitz/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" /> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" /> </head> <body> <div class="header"> <div class="container"> <!-- Header --> <div class="login pull-right"> <th1> if {[info exists login]} { html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n" } else { html "<a class='button' href='$home/login'>Login</a>\n" } </th1> <div> <h2><small>$title</small></h2> </div> </div> <div class='logo'> <img src='$logo_image_url' /> <th1> if {[anycap jor]} { html "<a class='rss' href='$home/timeline.rss'></a>" } </th1> </div> <!-- Main Menu --> <div class="mainmenu"> <ul> <th1> proc menulink {url name} { upvar current_page current upvar home home if {[string range $url 0 [string length $current]] eq "/$current"} { html "<li class='active'>" } else { html "<li>" } html "<a href='$home$url'>$name</a></li>\n" } menulink $index_page Home if {[anycap jor]} { menulink /timeline Timeline } if {[hascap oh]} { menulink /dir?ci=tip Files } if {[hascap o]} { menulink /brlist Branches menulink /taglist Tags } if {[hascap r]} { menulink /ticket Tickets } if {[hascap j]} { menulink /wiki Wiki } if {[hascap o]} { menulink /help Help } if {[hascap s]} { menulink /setup Admin } elseif {[hascap a]} { menulink /setup_ulist Users } </th1> </ul> </div> <!-- end div mainmenu --> </div> <!-- end div container --> </div> <!-- end div header --> <div class="middle max-full-width"> <div class="container"> |
Added skins/blitz/rss_20.png.
cannot compute difference between binary files
Added skins/blitz/ticket.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | <h4>$<title></h4> <table class="tktDsp"> <tr><td class="tktDspLabel">Ticket UUID</td> <th1> if {[info exists tkt_uuid]} { if {[hascap s]} { html "<td class='tktDspValue' colspan='3'>$tkt_uuid " html "($tkt_id)</td></tr>\n" } else { html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n" } } else { if {[hascap s]} { html "<td class='tktDspValue' colspan='3'>Deleted " html "(0)</td></tr>\n" } else { html "<td class='tktDspValue' colspan='3'>Deleted</td></tr>\n" } } </th1> <tr><td class="tktDspLabel">Status</td><td class="tktDspValue"> $<status> </td> <td class="tktDspLabel">Type</td><td class="tktDspValue"> $<type> </td></tr> <tr><td class="tktDspLabel">Severity</td><td class="tktDspValue"> $<severity> </td> <td class="tktDspLabel">Priority</td><td class="tktDspValue"> $<priority> </td></tr> <tr><td class="tktDspLabel">Subsystem</td><td class="tktDspValue"> $<subsystem> </td> <td class="tktDspLabel">Resolution</td><td class="tktDspValue"> $<resolution> </td></tr> <tr><td class="tktDspLabel">Last Modified</td><td class="tktDspValue"> <th1> if {[info exists tkt_datetime]} { html $tkt_datetime } </th1> </td> <th1>enable_output [hascap e]</th1> <td class="tktDspLabel">Contact</td><td class="tktDspValue"> $<private_contact> </td> <th1>enable_output 1</th1> </tr> <tr><td class="tktDspLabel">Version Found In</td> <td colspan="3" valign="top" class="tktDspValue"> $<foundin> </td></tr> <th1> if {[info exists comment]} { if {[string length $comment]>10} { html { <tr> <td class="tktDescLabel">Description</td> <td class="tktDescValue" colspan="3"> } if {[info exists plaintext]} { set r [randhex] wiki "<verbatim-$r links>\n$comment\n</verbatim-$r>" } else { wiki $comment } html "</td></tr>\n" } } </th1> </table> <div class="tktComments"> <th1> set seenRow 0 set alwaysPlaintext [info exists plaintext] query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin, mimetype as xmimetype, icomment AS xcomment, username AS xusername FROM ticketchng WHERE tkt_id=$tkt_id AND length(icomment)>0} { if {$seenRow eq "0"} { html "<h5>User Comments</h5>\n" set seenRow 1 } html "<div class='tktComment'>\n" html "<div class='tktCommentHeader'>\n" html "<div class='pull-right'>$xdate</div>\n" html "<span class='tktCommentLogin'>[htmlize $xlogin]</span>" if {$xlogin ne $xusername && [string length $xusername]>0} { html " (claiming to be <span class='tktCommentLogin'>[htmlize $xusername]</span>)" } html " commented</div>\n" html "<div class='tktCommentBody'>\n" if {$alwaysPlaintext || $xmimetype eq "text/plain"} { set r [randhex] if {$xmimetype ne "text/plain"} {html "([htmlize $xmimetype])\n"} wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n" } elseif {$xmimetype eq "text/x-fossil-wiki"} { wiki "<p>\n[string trimright $xcomment]\n</p>\n" } elseif {$xmimetype eq "text/html"} { wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki></p>\n" } else { set r [randhex] wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n" } html "</div><!-- end comment body -->\n" html "</div><!-- end comment -->\n" } </th1> </div> |
Added skins/blitz_no_logo/README.md.
> > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | ## Blitz Theme (no logo) Contributed by James Moger (james.moger@gitblit.com) This theme is inspired by my own project, [Gitblit](http://gitblit.com), and offered to the Fossil project. This theme embeds & uses an unmodified 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). This theme uses half of a heavily-modified version of [Skeleton](http://getskeleton.com) which is distributed under an [MIT license](https://github.com/dhg/Skeleton/blob/master/LICENSE.md). None of the responsive elements (media queries) are included at this time. The font used in the included Fossil logo image is [Trillium Web Light](http://www.google.com/fonts/specimen/Titillium+Web) @ 48px HTML color code #456a7a. The RSS feed icon is sourced from [Font-Awesome](https://fortawesome.github.io/Font-Awesome/icons) by Dave Gandy and is distributed under the [SIL OFL 1.1 ](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) license. |
Added skins/blitz_no_logo/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ /** * 1. Set default font family to sans-serif. * 2. Prevent iOS text size adjust after orientation change, without disabling * user zoom. */ html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ } /** * Remove default margin. */ body { margin: 0; } /* HTML5 display definitions ========================================================================== */ /** * Correct `block` display not defined for any HTML5 element in IE 8/9. * Correct `block` display not defined for `details` or `summary` in IE 10/11 * and Firefox. * Correct `block` display not defined for `main` in IE 11. */ article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } /** * 1. Correct `inline-block` display not defined in IE 8/9. * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. */ audio, canvas, progress, video { display: inline-block; /* 1 */ vertical-align: baseline; /* 2 */ } /** * Prevent modern browsers from displaying `audio` without controls. * Remove excess height in iOS 5 devices. */ audio:not([controls]) { display: none; height: 0; } /** * Address `[hidden]` styling not present in IE 8/9/10. * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. */ [hidden], template { display: none; } /* Links ========================================================================== */ /** * Remove the gray background color from active links in IE 10. */ a { background-color: transparent; } /** * Improve readability when focused and also mouse hovered in all browsers. */ a:active, a:hover { outline: 0; } /* Text-level semantics ========================================================================== */ /** * Address styling not present in IE 8/9/10/11, Safari, and Chrome. */ abbr[title] { border-bottom: 1px dotted; } /** * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. */ b, strong { font-weight: bold; } /** * Address styling not present in Safari and Chrome. */ dfn { font-style: italic; } /** * Address variable `h1` font-size and margin within `section` and `article` * contexts in Firefox 4+, Safari, and Chrome. */ h1 { font-size: 2em; margin: 0.67em 0; } /** * Address styling not present in IE 8/9. */ mark { background: #ff0; color: #000; } /** * Address inconsistent and variable font size in all browsers. */ small { font-size: 80%; } /** * Prevent `sub` and `sup` affecting `line-height` in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } /* Embedded content ========================================================================== */ /** * Remove border when inside `a` element in IE 8/9/10. */ img { border: 0; } /** * Correct overflow not hidden in IE 9/10/11. */ svg:not(:root) { overflow: hidden; } /* Grouping content ========================================================================== */ /** * Address margin not present in IE 8/9 and Safari. */ figure { margin: 1em 40px; } /** * Address differences between Firefox and other browsers. */ hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; } /** * Contain overflow in all browsers. */ pre { overflow: auto; } /** * Address odd `em`-unit font size rendering in all browsers. */ code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } /* Forms ========================================================================== */ /** * Known limitation: by default, Chrome and Safari on OS X allow very limited * styling of `select`, unless a `border` property is set. */ /** * 1. Correct color not being inherited. * Known issue: affects color of disabled elements. * 2. Correct font properties not being inherited. * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. */ button, input, optgroup, select, textarea { color: inherit; /* 1 */ font: inherit; /* 2 */ margin: 0; /* 3 */ } /** * Address `overflow` set to `hidden` in IE 8/9/10/11. */ button { overflow: visible; } /** * Address inconsistent `text-transform` inheritance for `button` and `select`. * All other form control elements do not inherit `text-transform` values. * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. * Correct `select` style inheritance in Firefox. */ button, select { text-transform: none; } /** * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` * and `video` controls. * 2. Correct inability to style clickable `input` types in iOS. * 3. Improve usability and consistency of cursor style between image-type * `input` and others. */ button, html input[type="button"], /* 1 */ input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ } /** * Re-set default cursor for disabled elements. */ button[disabled], html input[disabled] { cursor: default; } /** * Remove inner padding and border in Firefox 4+. */ button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } /** * Address Firefox 4+ setting `line-height` on `input` using `!important` in * the UA stylesheet. */ input { line-height: normal; } /** * It's recommended that you don't attempt to style these elements. * Firefox's implementation doesn't respect box-sizing, padding, or width. * * 1. Address box sizing set to `content-box` in IE 8/9/10. * 2. Remove excess padding in IE 8/9/10. */ input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } /** * Fix the cursor style for Chrome's increment/decrement buttons. For certain * `font-size` values of the `input`, it causes the cursor style of the * decrement button to change from `default` to `text`. */ input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } /** * 1. Address `appearance` set to `searchfield` in Safari and Chrome. * 2. Address `box-sizing` set to `border-box` in Safari and Chrome * (include `-moz` to future-proof). */ input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; } /** * Remove inner padding and search cancel button in Safari and Chrome on OS X. * Safari (but not Chrome) clips the cancel button when the search input has * padding (and `textfield` appearance). */ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } /** * Define consistent border, margin, and padding. */ fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } /** * 1. Correct `color` not being inherited in IE 8/9/10/11. * 2. Remove padding so people aren't caught out if they zero out fieldsets. */ legend { border: 0; /* 1 */ padding: 0; /* 2 */ } /** * Remove default vertical scrollbar in IE 8/9/10/11. */ textarea { overflow: auto; } /** * Don't inherit the `font-weight` (applied by a rule above). * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. */ optgroup { font-weight: bold; } /* Tables ========================================================================== */ /** * Remove most spacing between table cells. */ table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; } /* * Blitz * * Skin inspired by Gitblit with heavily-modified excerpts from Skeleton 2.0.4. * Blitz is authored by james.moger@gitblit.com. * * Skeleton is authored by Dave Gamache and is distributed under the MIT license. * http://getskeleton.com * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ html { /* 62.5% so that the REM values are base 10px. */ /* 1.5rem = 15px */ font-size: 62.5%; } /* Typography ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ h1, h2, h3, h4, h5, h6 { margin: 0; margin-bottom: 1rem; font-weight: 700; } h1 { font-size: 3.0rem; line-height: 1.2; } h2 { font-size: 2.6rem; line-height: 1.25; } h3 { font-size: 2.4rem; line-height: 1.3; } h4 { font-size: 2.0rem; line-height: 1.35; } h5 { font-size: 1.6rem; line-height: 1.5; } h6 { font-size: 1.4rem; line-height: 1.6; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { font-size: 0.75em; font-weight: 400; color: #ccc; } pre, code { font-size: 1.2rem; } body { font-size: 1.4em; /* currently ems cause chrome bug misinterpreting rems on body element */ line-height: 1.5; font-weight: 400; font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; color: #333; background-color: #f8f8f8; } /* Spacing ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ button, .button { margin-bottom: 1rem; } input, textarea, select, fieldset, pre, blockquote, dl, figure, table, p, ul, ol { margin-bottom: 1rem; } p { margin-top: 0; } hr { margin-top: 3rem; margin-bottom: 3.5rem; border-width: 0; border-top: 1px solid #ccc; } /* Buttons ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .button, button, input[type="button"], input[type="reset"], input[type="submit"] { display: inline-block; height: 3.3rem; padding: 0 2.2rem; color: #555 !important; text-align: center; font-size: 1.1rem; font-weight: 700; line-height: 3.3rem; letter-spacing: .08rem; text-transform: uppercase; text-decoration: none; white-space: nowrap; background-color: transparent; border-radius: 4px; border: 1px solid #ccc; cursor: pointer; box-sizing: border-box; } .button:hover, button:hover, input[type="button"]:hover, input[type="reset"]:hover, .button:focus, button:focus, input[type="button"]:focus, input[type="reset"]:focus { color: #444 !important; background-color: #eee; border-color: #aaa; outline: 0; } input[type="submit"] { color: white !important; background-color: #446979; border-color: #446979; } input[type="submit"]:hover, input[type="submit"]:focus { color: white !important; background-color: #648898; border-color: #648898; } /* Forms ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ input[type="email"], input[type="number"], input[type="search"], input[type="text"], input[type="tel"], input[type="url"], input[type="password"], textarea, select { height: 3.3rem; padding: 6px 10px; background-color: #fff; border: 1px solid #ddd; border-radius: 4px; box-shadow: none; box-sizing: border-box; } /* Removes awkward default styles on some inputs for iOS */ input[type="email"], input[type="number"], input[type="search"], input[type="text"], input[type="tel"], input[type="url"], input[type="password"], textarea { -webkit-appearance: none; -moz-appearance: none; appearance: none; } textarea { height: inherit; min-height: 65px; padding-top: 6px; padding-bottom: 6px; } input[type="email"]:focus, input[type="number"]:focus, input[type="search"]:focus, input[type="text"]:focus, input[type="tel"]:focus, input[type="url"]:focus, input[type="password"]:focus, textarea:focus, select:focus { border: 1px solid #aaa; outline: 0; } label, legend { display: block; margin-bottom: .5rem; font-weight: 700; } fieldset { padding: 0; border-width: 0; } input[type="checkbox"], input[type="radio"] { display: inline; } /* Links ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ a { color: #446979; text-decoration: none; } a:hover { text-decoration: underline; } /* Lists ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ ul { list-style: square; } ol { list-style: decimal; } ol, ul { padding-left: 3rem; margin-top: 0; } li { margin-bottom: 0.5rem; } /* Nested Lists ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ ul ul, ul ol, ol ol, ol ul { margin: 1rem 0 1rem 2rem; } /* Code ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ code, kbd { padding: .2rem .5rem; margin: 0 .2rem; white-space: nowrap; background: #f8f8f8; border: 1px solid #ccc; border-radius: 4px; } pre > code { display: block; padding: 1rem 1.5rem; white-space: pre; } pre.verbatim { background-color: inherit; white-space: pre-wrap; } /* Blockquote ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ blockquote { padding: 0px 20px; margin: 0 0 20px; border-left: 4px solid #ccc; } /* Tables ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ th, td { padding: 6px 5px; text-align: left; border-bottom: 1px solid #ddd; } th:first-child, td:first-child { padding-left: 0; } th:last-child, td:last-child { padding-right: 0; } /* * Blitz Page Layout Design * * html > body > header > container > mainmenu * middle > container > submenu & content * footer > container > generation stats, fossil logo, version * ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ /* Container * Represents the usable layout space for header, middle, and footer. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .container { position: relative; width: 100%; max-width: 900px; margin: 0 auto; box-sizing: border-box; } /* Header * Div displayed at the top of every page. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .header { color: #666; font-weight: 400; padding-top: 10px; border-width: 0px; border-top: 4px solid #446979; border-bottom: 1px solid #ccc; } .header .logo { display: inline-block; } .header .login { padding-top: 2px; text-align: right; } .header .login .button { margin: 0; } .header h1 { margin: 0px; color: #666; display: inline-block; } .header .logo h1 { display: inline-block; } .header .title h1 { padding-bottom: 10px; } .header h1 small, .header h2 small, .header .login { color: #888; } .header a.rss { display: inline-block; padding: 10px 15px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMNDhwn05VjawAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAGlSURBVDjLrdPfb8xREAXwT7tIl+paVNaPJghCKC8kXv0XXvyNXsRfwYPQJqVKiqykWFVZXd12vcxNJtduUtJJvrm7984998ycMxxwNGI9jPs4j7nY+/U/gIdiPYO71dk21rCE7r8ybOHGmMfmcRNnsbEf1gXwNzqYSXs5WljEMXzAaBLg1Ji9Js7hOi6OeeAznqC/X8AcMyHWYpX7E4/Rm1QyHMdefCWGeI/VcMDR2D8S7Fci5y/AeTzCPVyLi1sYJAut4BTaiX0n9kc14MmkcjPY3I5LXezGtxqKtyJ3Lir6VAM2AmCq6m8Hl6PsQTB5hyvxmMhZxk4G3MZLfAwLtdNZM9rwOs528TVVNB3ga7UoQ2wGmyWciFaU0VwIJiP8iL6Xfp7GK+w0JthliDep8UKonTSGvbBTaU8f3QzYxgPcCsBvWK9E6OBFCNGPVjTTqC430p+H6fLVGLGtmIw7SbwevqT+XkgVPJ9Otpmtyl6I9XswLXEp/d6oPN0ugJu14xMLob4kgPRYjtkCOMDTUG+AZ3ibEtfDLorfEmAB3UuTdXDxBzUUZV+B82aLAAAAAElFTkSuQmCC); background-position: center center; background-repeat: no-repeat; } /* Middle * Center div displayed between header and footer. Contains per-page content. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .middle { background-color: white; padding-bottom: 20px; max-width: 100%; box-sizing: border-box; } /* Content * Displayed below submenu within the middle div. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .content { padding-top: 15px; } .content a { color: #002060; } /* Footer * Displayed after the middle div and forms the page bottom. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .footer { padding: 10px 0 60px; border-top: 1px solid #ccc; background-color: #f8f8f8; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABGCAQAAADxl9ugAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffAwkQBRPw+yfrAAAAPHRFWHRDb21tZW50ACBJbWFnZSBnZW5lcmF0ZWQgYnkgRVNQIEdob3N0c2NyaXB0IChkZXZpY2U9cG5tcmF3KQqV01S1AAANg0lEQVRo3s2aa3RVRZbH/3tXnftIbhJCeJMXyEsNCsgrSBzAYXyt0YVj2zSOonbLqNNO+25FB2kdoBVdrp6l09P2NNpOL/ExSrtUdGRU5JEQgggCggIhQIBAIIGQ5N57TlXt+ZAICY+AGCT76zlV91dVe+/a+38uDQ7dYmrxw41QocJuni28vWTedEa7Gd9iatplos7Y2mfDRED6A2G0I2At6AdPIiA84HgbLhx1o9QX9Zoh7QjYHpOkYJYrfNyOKH0O/f2FJpfkSRmMCNqDk9rDXwTAuxK7h3uFf7vXxC6QVO8w19taNOT4Y0wu1sCdW0AA6IZ73bCUaI/GHuqnsJqcEqVrzSo+wNtLdgAPsz6jeVURtQ9gA8bQT8xztW7X2u3I4G6UdA0cohxcSAN7nt939/zDQzl0LnfwaLpxqFT/i7w7XLYXhoUQ+YFW3pJlC0/nt6jZZb4zjXY2AaGXvQOz/gMY09NM4C6ixRL7OU3PTm4ORdiIjyLsJvq1Z28Hj5rBM82xMTqPuhHznmU72vq12W5M3+LykQWUW9pirzXOmmk8wjE87kb8HPmSKlY+wY62R9jhI69xf/AmYiEfiXs+e4CCAZiDUc9wP6cRIJn8tq23PQClbwap9Ivk7usiGj/CDs5xl3Qe/EiQCEWsgXLlq8sfamM73osMna43ef/pfm731Hsxv+Z00ozFzaJpF+gMrsNB2BwL3cGpSsNRWHbmvTQNiTbev9q4xXszMLzqpdgDNjxv/YjmvHlSQIc5brl8nrqT5pllMpHM9wRc4gU3oC8JyDLt8P78ZLyM2lrmAeTg5YrKdf1S1N9QTs6At1YM49DJo1gwWy7uE5kKBQHbler9K8R+L8API2qmFoDY7ejxpxGJxtMctyZcfYvJDYX4i5LXp/MJAS2edkM6edNUDuLNOdMDmz913bIw8RidKO/1w+3NQfePbHQjdbd/NKOjbq7UqQOmYtXb/+BdbGc4AJjPX53CXe6UGweYWxUR8N8rNkzn4wAFc9yYPvGbI+mttoycpo14fWbD0hZv+njOAcBl6Yl8jiEMbSKhqGPypZ5N0B2kDxrjOjF0QuI2oRqj2z6rAebx5pNiRrEo0jhV9wFkP//xpkPHAU6UGdnBLyR8jMOQs6xwcMAznVys+Uqa5YCCnilFkuVSkeKlGKfDED8JaI+VifMWR9LPC/u+UkoZA6edOezVu83pny4yD6qQnCxhDzk/MhUED+8PWHJMkAgWROvvQoxOcEdayynVY9/5tIgAwsfIvaD3XaFx6M6ddMx3WlurCGICiAIhMLxB1VM+wQRMDOeYjHCUMum85OW909/acOlJgqaI3qrulU9dYGlgzVI1tsVrETzlOt3MeUGCudVoCgJmAhFU78F+6Usu0V39iylSHCRZMROM0do5pUDWQamEPZTy5oqyXVt77bG94HGEyTkCE8BsjLOcl11Uvf7V+qvIP86jgWH8zhc9LpKYU7qRhH5NTRknwFw3+jpMQCIItG69PCLfZ1YKAMlS/7PQDCc2qbRzSjET4slINBAvhsPxjarKKy/9FhjZG8Ol4tINi/uFzkcfl8sEnwExATGRY73Ze2VpAHxHoPFki7r2iljN3a6LqqOxnZ+vXUBNwTF6uL0e0Nr3tSYWpqNhQtaIaA0A1oZftuf5Y1xmCMRO2IM1VXqf3aYr5IDXODTx7+43PAjPFcit1MANeHfFxuFpSFW9TbbORpaNEqt6/5tIoxuBOO049NHXNTN5lFwlE3o09JUMlWKdSmJ/UC8TvRwqjNzoVwNIw3upcluyq4hSAq3Idyt5HOyRIDGuGdA42un+6wv/Cq7sFA1F/B4N/5MEgL/yOsSPlFSMOzD5JhkMEQ8l8u41Job7hVoExsiRNMU1Biac4oqD92Np8Xt0BgUiEOeIHLyobbQb9U/8fSAItsFdJv1wECASB5L92xb2yefcE3Q+jgsof//m3+PO2i7YhgV0HisAK48UnE03EaT67axMyjMNXpFkf/za8r3VPP2I46Ri+aYDW0z3SMT6NJb62wPiJEFhEoFCYIhNoyS4VF1CBKArPu6MKWysBZhFVFrD7yri2VkuR4QZAIkTMAOAiDIY9KvFQ3gVPqXVFD9JSbSS3vP7b/QzKY8SlO6G5e2fv/dozvDxciKvCqOYrIURT/XU2+0SKrdVqtpVojz4Sq1Vi1duV0UE+PSEy33AhRRZAzDDc4vXrB1xKU0gRUR0LCCRi/be8XZ12/0M41Ka5K/5JjA80PlQKMjfeVVN9ZG0NZbmH8xf5Y8nJxIKmYC7SmLlexeVp2wKb+y++f6KZ3c/03g+qSISPONGDqHRZOk7QGde6XsrjTP+kXTTCpCVGErZteZUDRdhD3Ls65tz64J85ZGyQ0tXDAzckadFlOtv+ZQLoYMgHHaGcrJ7fb56inR3WVJJRVTdVM3kQSh5r1hjlbIGIKVX6ammE1vnmImh4Y4BZHGcvmvx5XQ6BcRIfnPHxV82ZnFUYsh+bWXLzMuwquGwCnvdBUTkqHcPmb+l5cJZcKervlwc8N04sbYwSJC1tsnneemJqh32LksZflr1SUh+pT6q+/ov/krztitDq1s4hP1p7mbfuFfkEKJgSejLh/Wa7Y5GJsdQmEYDoVocDCvnLCvnwPDMzOK/uiVoPS8IxoQi409D2xAU4Hd2xE2DZ3gjvcpVZY+0iqgAbFh7F9grp84xf5D6QFOE/rYo1vto4z6ZVg/COHKAiGJrACaljNHaiaqTp0NeXlr37Ydz/ahSLY9YzLIPMzlxSrxMzM/ochfnO8UHvJJpgTnm+UE5GKMeOu3LrLJPd32+Z1FamfczKli36jzTtHplvPg46o2WgNwEaOqoTk8Jxsu4+vGUruDkCCCJkg171g2ntqMkjrmuYUBwG7LglIq/U7Zr1HEjJrtiay9imIzsqt37gb4/RSZlSsP88iZf5T3kX2xOuBEck2yp13F32NbDuZbHqSjwFrzJbeP5eN6NHOJuoQyXRIZ7Y+3Gh08wohjpW/VOkFMyqagHILnOSAOuflh1bqKggew524Zs1ex+R6cmz1W7Z6clvzzF8T7rLsvnSfBgJVO9cN+ax4/IRwazZLZrqmOSWJjAR0hCdFc/F2AtToTos34POQHAkVw2Sp2m7EKwOEyLVj59w6EtpyjdFYSCCYhaC0n9fcmmr5pb8QBrQl0wqn/hNc82J8Rfc8lWVwVGwLeNDVPTdW30+KZN0Sab5JTBqOBBowIHzeZVJcBjXHdKXfQp93+DgwuIgy9QtqJ8evPhOtRRzZi30iKfBZPxQVNhofDP6sUXh05zA5MH+WKdpAgAJ/lAChqhyQmd5HgFChFoMvar0AaKq8qSGmA6o03lVFCEqx1Q0Ce0075hkgvW93EPNeMZ3Cl93eRl5Q8GVjoBs1tUN1l/rvkn1VOuNXUIAyATv6zz3bXPEo3t7N/L6bBirfV0MimktOclgnDUJHiTWxo9oJOZQT/TDUk0nIYfFODFaMMN4WFqbnEl8BzXtlBX++ATr2KW3eC9kbg/9d2l6/qmdxrq9bdRkBwMtrgd+lpkpxwyWSwAkvRKSfl0phf4l+6acGVeak6QQbCJYH/Gwci+zPi/+oMFEJpLdTDfQ8IpTTl8r/lkQuniX3oR98mjX65sJQZ0wYNu1NU0wZ8RjSUf0Qo22aCU1oAoCazTFe5bdZ1pVIqcv2B12XSm6awxCFNaichCr1I1auDOQFvKxzQHFD4mexGXNfds/PoYB+iMhxzwIC8vcJe7HKVIrBEoLRQkuVL+Ii70lDlMUOR/uHrJdNaAwXo82orksR8gfVUAGFkgy5JfhKdEKncelwhq8SinwMeidX+303VHuvOgCGSRRF2o6vb6FyKmQTGRNTraQt1qJ6EaAPA4V22MYH20wXx+aCyfKFvFAcymiYfCh/YhSQZACFHpBB/l6On2l0s/AgTBWdIHHbpIulzfWDbvAW4r3gPUIwWZ0lW6SoaE0AgDYG6Six0R4OjgWRMwGUAVHuUz+SS2iNQ+XSMAvKqzrLCemdtUo3ifWhpoTi7b/RSfVRH9zBc2y43umXVgtImf3R08c7zCIeYpVoPOtoh+ZjYEhd3kWtV4IHSldEjAV7U/ATHxig89Tx0QUFCtvUJisxnNknuHArSY4+xtNuk4ZQWai4wOBThJRqZJvhYXtptnNTVAHQnQYbT4k7QI6/Vd6w6f/U9h39cUgEg+BCG3rk/AHQ9wlhsz1IUCI3tRPrDVtdlhzO9rxPNke+nuXR0TUIdAMOrDlkJshwEM4QId98C8vKTmUUJHAwww03VNT8ny9hd/cJ9qCaU7QnrpjXvcsKuT6W7VdcvHc+sq95wDCuZILxp9t+TZJWVL/56PPdJzDhjDhG75UyUrsXXtBw9z/PjAObd3729leA++mTOldO07dyl9ghbmnALmYpjybndhO3/VV/dx9IQdlj53YQEAo2din3p1cv1VfDIF8EfvSdJQSZvU3ljGJJOvNuH94kOPs22jwWq3P5edTryG8G/OdtsxOPiZd6O1ppTeK0n43Hb/9yPuYBhPuFFXumEq3231S+ibL/e+yhtP2Zz+iD74hCu83vblr+W1yJ5LgguxmzedRu/8//AUSaqMTR1xAAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: center top 10px; } .footer a { color: #3b5c6b; } /* Main Menu * Displayed in header, contains repository links. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .mainmenu { clear:both; } .mainmenu ul { list-style: none outside; display: block; position: relative; border-top: 1px solid #ccc; padding: 0; } .mainmenu li { outline: 0; display: block; float: left; margin: 0; } .mainmenu li.active { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB90FDxEXAZ2XRzAAAABJSURBVCjPY2CgBzhz5sx/QmoYiTXAxMSEkWRDsLkAl0GMpHoBm0EoAlu3bmUQFxcnGAboBjEhc4gxAJtLGUmJBVwuYiTXAGSDAIx5IBObnuVxAAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: center bottom; } .mainmenu li a { color: #3b5c6b; display: block; padding: 10px 15px; } .mainmenu li.active a { font-weight: bold; } .mainmenu li:hover { background-color: #eee; } /* Submenu * Displayed in the middle div. Contains page-specific form controls. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .submenu { padding: 10px 0px; border-bottom: 1px solid #ddd; } .submenu input, .submenu select { margin: 0 0 0 5px; } .submenu a { color: #3b5c6b; padding: 5px 15px; text-decoration: none; border: 1px solid transparent; border-radius: 5px; } .submenu a:hover { border: 1px solid #ccc; } /* Section * Cap/header to distinguish a section. Displayed within a content div. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .section { font-weight: bold; background-color: #f5f5f5; border: 1px solid #ccc; padding: 9px 10px 10px; margin: 10px 0; } /* Section Menu * Div of buttons/links, displayed with a section div within a content div. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .sectionmenu { border: 1px solid #ccc; border-top: 0; margin-top: -10px; margin-bottom: 10px; padding: 5px; text-align: center; } .sectionmenu a { display: inline-block; margin-top: 5px; margin-right: 1em; } /* File browser * Repository tree navigation. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ ul.browser { list-style: none; } ul.browser li.dir { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExABnLjGZQAAAEFJREFUOMtjYKAQMIaGhv4npGj16tWMuORYGBgYGOZW+eDUnNy2Ba/hLMQ4E58rCRpAyHVMlAbiqAGjBhCdmWgKAHp4Dh0ZusP3AAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: 0px center; padding-left: 22px; padding-top: 2px; } ul.browser li.file { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExMaPfBcSgAAAFNJREFUOMvtkzEOwDAIA02VL5pHwiOTJZFQmkqFOTex+CwPCCYkAaDjB+4u65ZdYGafQVV9SR4kWQUke0mwS1o2HGcAQKs0R1lpQuQKruD4jVnBAG/cGRqf0U66AAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: 0px center; padding-left: 22px; padding-top: 2px; } div.filetreeline { display: table; width: 100%; white-space: nowrap; } /* tree-view top-level list */ .filetree > ul { display: inline-block; } /* tree-view lists */ .filetree ul { margin: 0; padding: 0; list-style: none; } /* tree-view collapsed list */ .filetree ul.collapsed { display: none; } /* tree-view lists below the root */ .filetree ul ul { position: relative; margin: 0 0 0 21px; } /* tree-view lists items */ .filetree li { position: relative; margin: 0; padding: 0; } /* tree-view node lines */ .filetree li li:before { content: ''; position: absolute; top: -.8em; left: -14px; width: 16px; height: 1.5em; border-left: 1px solid #ccc; border-bottom: 1px solid #ccc; } /* tree-view directory lines */ .filetree li > ul:before { content: ''; position: absolute; top: -1.5em; bottom: 0; left: -35px; border-left: 1px solid #ccc; } /* hide lines for last-child directories */ .filetree li.last > ul:before { display: none; } .filetree a { position: relative; z-index: 1; display: table-cell; min-height: 16px; padding-left: 22px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExMaPfBcSgAAAFNJREFUOMvtkzEOwDAIA02VL5pHwiOTJZFQmkqFOTex+CwPCCYkAaDjB+4u65ZdYGafQVV9SR4kWQUke0mwS1o2HGcAQKs0R1lpQuQKruD4jVnBAG/cGRqf0U66AAAAAElFTkSuQmCC); background-position: center left; background-repeat: no-repeat; } .filetree .dir > div.filetreeline > a { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAVQBVAFV4xrLkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMLExABnLjGZQAAAEFJREFUOMtjYKAQMIaGhv4npGj16tWMuORYGBgYGOZW+eDUnNy2Ba/hLMQ4E58rCRpAyHVMlAbiqAGjBhCdmWgKAHp4Dh0ZusP3AAAAAElFTkSuQmCC); } /* Label-Value table * Displayed on the Check-in & Admin pages. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.label-value th { vertical-align: middle; } /* Branches table ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .brlist table td { padding: 5px; } /* Timeline * Displays chronologically-ordered check-ins with a branch graph. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ tr.timelineCurrent { border-left: 2px solid orange; background-color: #ffc; } tr.timelineSelected { border-left: 2px solid orange; background-color: #ffffe8; } tr.timelineCurrent td.timelineTableCell { } tr.timelineSpacer { } div.timelineDate { font-weight: bold; white-space: nowrap; } a.timelineHistLink { text-transform: lowercase; } span.timelineComment { padding: 0px 5px; } /* Login/Loguot ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.login_out { } table.login_out .login_out_label { font-weight: 700; text-align: right; } table.login_out td { border: 0; } /* Diff displays ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ pre.udiff, table.sbsdiffcols { width: 100%; overflow: auto; border: 1px solid #ccc; padding: 0px 5px; font-size: 1rem; } pre.udiff:focus, table.sbsdiffcols:focus { outline: none; } /* Ticket Reports ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.report { width: 100%; cursor: auto; border-radius: 4px; border: 1px solid #ccc; margin: 0 0 1em 0; } .report td, .report th { border: 0; font-size: .9em; padding: 5px; } .report th { cursor: pointer; } .report thead+tbody tr:hover { background-color: #f5f9fc !important; } /* Ticket page ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ table.tktDsp { border-top: 1px solid #ccc; border-left: 1px solid #ccc; width: 100%; margin: 15px 0px 10px 0px; } td.tktDspLabel, td.tktDescLabel { width: 70px; text-align: right; overflow: hidden; font-weight: 700; padding: 10px; background-color: #f8f8f8; } td.tktDescLabel { vertical-align: top; } td.tktDspValue, td.tktDescValue { text-align: left; vertical-align: top; border: 1px solid #ccc; padding: 10px; } td.tktDspValue pre, td.tktDescValue pre, td.tktDspValue code, td.tktDescValue code { white-space: pre-wrap; } div.tktComments { width: 100%; margin: 30px 0px 10px 0px; } div.tktComment { } div.tktCommentHeader { border: 1px solid #ccc; background-color: #f8f8f8; padding: 10px 10px; margin-bottom: 10px; } span.tktCommentLogin { display: inline-block; font-weight: 700; color: #002060; } div.tktCommentBody { margin: 10px 40px 30px; } /* User setup table ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ td.usetupEditLabel { font-weight: 700; } /* Utilities ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .full-width { width: 100%; box-sizing: border-box; } .max-full-width { max-width: 100%; box-sizing: border-box; } .pull-right { float: right; } .pull-left { float: left; } /* Clearing ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ .container:after, .mainmenu:after, .row:after, .u-cf { content: ""; display: table; clear: both; } |
Added skins/blitz_no_logo/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 0 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 |
Added skins/blitz_no_logo/footer.txt.
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | </div> <!-- end div container --> </div> <!-- end div middle max-full-width --> <div class="footer"> <div class="container"> <div class="pull-right"> <a href="http://fossil-scm.org">Fossil version $manifest_version $manifest_date</a> </div> This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s </div> </div> </body> </html> |
Added skins/blitz_no_logo/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" /> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" /> </head> <body> <div class="header"> <div class="container"> <div class="login pull-right"> <th1> if {[info exists login]} { html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n" } else { html "<a class='button' href='$home/login'>Login</a>\n" } </th1> </div> <div class='title'> <h1>$<project_name> <th1> if {[anycap jor]} { html "<a class='rss' href='$home/timeline.rss'></a>" } </th1> <small> $<title></small></h1> </div> <!-- Main Menu --> <div class="mainmenu"> <ul> <th1> proc menulink {url name} { upvar current_page current upvar home home if {[string range $url 0 [string length $current]] eq "/$current"} { html "<li class='active'>" } else { html "<li>" } html "<a href='$home$url'>$name</a></li>\n" } menulink $index_page Home if {[anycap jor]} { menulink /timeline Timeline } if {[hascap oh]} { menulink /dir?ci=tip Files } if {[hascap o]} { menulink /brlist Branches menulink /taglist Tags } if {[hascap r]} { menulink /ticket Tickets } if {[hascap j]} { menulink /wiki Wiki } if {[hascap o]} { menulink /help Help } if {[hascap s]} { menulink /setup Admin } elseif {[hascap a]} { menulink /setup_ulist Users } </th1> </ul> </div> <!-- end div mainmenu --> </div> <!-- end div container --> </div> <!-- end div header --> <div class="middle max-full-width"> <div class="container"> |
Added skins/blitz_no_logo/ticket.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | <h4>$<title></h4> <table class="tktDsp"> <tr><td class="tktDspLabel">Ticket UUID</td> <th1> if {[info exists tkt_uuid]} { if {[hascap s]} { html "<td class='tktDspValue' colspan='3'>$tkt_uuid " html "($tkt_id)</td></tr>\n" } else { html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n" } } else { if {[hascap s]} { html "<td class='tktDspValue' colspan='3'>Deleted " html "(0)</td></tr>\n" } else { html "<td class='tktDspValue' colspan='3'>Deleted</td></tr>\n" } } </th1> <tr><td class="tktDspLabel">Status</td><td class="tktDspValue"> $<status> </td> <td class="tktDspLabel">Type</td><td class="tktDspValue"> $<type> </td></tr> <tr><td class="tktDspLabel">Severity</td><td class="tktDspValue"> $<severity> </td> <td class="tktDspLabel">Priority</td><td class="tktDspValue"> $<priority> </td></tr> <tr><td class="tktDspLabel">Subsystem</td><td class="tktDspValue"> $<subsystem> </td> <td class="tktDspLabel">Resolution</td><td class="tktDspValue"> $<resolution> </td></tr> <tr><td class="tktDspLabel">Last Modified</td><td class="tktDspValue"> <th1> if {[info exists tkt_datetime]} { html $tkt_datetime } </th1> </td> <th1>enable_output [hascap e]</th1> <td class="tktDspLabel">Contact</td><td class="tktDspValue"> $<private_contact> </td> <th1>enable_output 1</th1> </tr> <tr><td class="tktDspLabel">Version Found In</td> <td colspan="3" valign="top" class="tktDspValue"> $<foundin> </td></tr> <th1> if {[info exists comment]} { if {[string length $comment]>10} { html { <tr> <td class="tktDescLabel">Description</td> <td class="tktDescValue" colspan="3"> } if {[info exists plaintext]} { set r [randhex] wiki "<verbatim-$r links>\n$comment\n</verbatim-$r>" } else { wiki $comment } html "</td></tr>\n" } } </th1> </table> <div class="tktComments"> <th1> set seenRow 0 set alwaysPlaintext [info exists plaintext] query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin, mimetype as xmimetype, icomment AS xcomment, username AS xusername FROM ticketchng WHERE tkt_id=$tkt_id AND length(icomment)>0} { if {$seenRow eq "0"} { html "<h5>User Comments</h5>\n" set seenRow 1 } html "<div class='tktComment'>\n" html "<div class='tktCommentHeader'>\n" html "<div class='pull-right'>$xdate</div>\n" html "<span class='tktCommentLogin'>[htmlize $xlogin]</span>" if {$xlogin ne $xusername && [string length $xusername]>0} { html " (claiming to be <span class='tktCommentLogin'>[htmlize $xusername]</span>)" } html " commented</div>\n" html "<div class='tktCommentBody'>\n" if {$alwaysPlaintext || $xmimetype eq "text/plain"} { set r [randhex] if {$xmimetype ne "text/plain"} {html "([htmlize $xmimetype])\n"} wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n" } elseif {$xmimetype eq "text/x-fossil-wiki"} { wiki "<p>\n[string trimright $xcomment]\n</p>\n" } elseif {$xmimetype eq "text/html"} { wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki></p>\n" } else { set r [randhex] wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n" } html "</div><!-- end comment body -->\n" html "</div><!-- end comment -->\n" } </th1> </div> |
Added skins/default/README.md.
> > > > > | 1 2 3 4 5 | This skin was contributed by Étienne Deparis. On 2015-03-14 this skin was promoted from an option to the default, which involved moving it from its original home in the skins/etienne1 directory into skins/default. |
Added skins/default/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | body { margin: 0 auto; min-width: 800px; padding: 0px 20px; background-color: white; font-family: sans-serif; font-size:14pt; -moz-text-size-adjust: none; -webkit-text-size-adjust: none; -mx-text-size-adjust: none; } a { color: #4183C4; text-decoration: none; } a:hover { color: #4183C4; text-decoration: underline; } hr { color: #eee; } .title { color: #4183C4; float:left; padding-top: 30px; padding-bottom: 10px; } .title h1 { display:inline; } .title h1:after { content: " / "; color: #777; font-weight: normal; } .content h1 { font-size: 1.25em; } .content h2 { font-size: 1.15em; } .content h2 { font-size: 1.05em; font-weight: bold; } .section { font-size: 1em; font-weight: bold; background-color: #f5f5f5; border: 1px solid #d8d8d8; border-radius: 3px 3px 0 0; padding: 9px 10px 10px; margin: 10px 0; } .sectionmenu { border: 1px solid #d8d8d8; border-radius: 0 0 3px 3px; border-top: 0; margin-top: -10px; margin-bottom: 10px; padding: 10px; } .sectionmenu a { display: inline-block; margin-right: 1em; } .status { float:right; font-size:.7em; padding-top:50px; } .mainmenu { font-size:.8em; clear:both; padding:10px; background:#eaeaea linear-gradient(#fafafa, #eaeaea) repeat-x; border:1px solid #eaeaea; border-radius:5px; } .mainmenu a { padding: 10px 20px; text-decoration:none; color: #777; border-right:1px solid #eaeaea; } .mainmenu a.active, .mainmenu a:hover { color: #000; border-bottom:2px solid #D26911; } .submenu { font-size: .7em; margin-top: 10px; padding: 10px; border-bottom: 1px solid #ccc; } .submenu a { padding: 10px 11px; text-decoration:none; color: #777; } .submenu a:hover { padding: 6px 10px; border: 1px solid #ccc; border-radius: 5px; color: #000; } .content { padding-top: 10px; font-size:.8em; color: #444; } .udiff, .sbsdiff { font-size: .85em !important; overflow: auto; border: 1px solid #ccc; border-radius: 5px; } .content blockquote { padding: 0 15px; } table.report { cursor: auto; border-radius: 5px; border: 1px solid #ccc; margin: 1em 0; } .report td, .report th { border: 0; font-size: .8em; padding: 10px; } .report td:first-child { border-top-left-radius: 5px; } .report tbody tr:last-child td:first-child { border-bottom-left-radius: 5px; } .report td:last-child { border-top-right-radius: 5px; } .report tbody tr:last-child { border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; } .report tbody tr:last-child td:last-child { border-bottom-right-radius: 5px; } .report th { cursor: pointer; } .report thead+tbody tr:hover { background-color: #f5f9fc !important; } td.tktDspLabel { width: 70px; text-align: right; overflow: hidden; } td.tktDspValue { text-align: left; vertical-align: top; background-color: #f8f8f8; border: 1px solid #ccc; } td.tktDspValue pre { white-space: pre-wrap; } .footer { border-top: 1px solid #ccc; padding: 10px; font-size:.7em; margin-top: 10px; color: #ccc; } div.timelineDate { font-weight: bold; white-space: nowrap; } span.submenuctrl, span.submenuctrl input, select.submenuctrl { color: #777; } |
Added skins/default/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/default/footer.txt.
> > > > > > | 1 2 3 4 5 6 | <div class="footer"> This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by Fossil version $manifest_version $manifest_date </div> </body></html> |
Added skins/default/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" /> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" /> </head> <body> <div class="header"> <div class="title"><h1>$<project_name></h1>$<title></div> <div class="status"><th1> if {[info exists login]} { html "$login — <a href='$home/login'>Logout</a>\n" } else { html "<a href='$home/login'>Login</a>\n" } </th1></div> </div> <div class="mainmenu"> <th1> proc menulink {url name} { upvar current_page current upvar home home if {[string range $url 0 [string length $current]] eq "/$current"} { html "<a href='$home$url' class='active'>$name</a>\n" } else { html "<a href='$home$url'>$name</a>\n" } } menulink $index_page Home if {[anycap jor]} { menulink /timeline Timeline } if {[hascap oh]} { menulink /dir?ci=tip Files } if {[hascap o]} { menulink /brlist Branches menulink /taglist Tags } if {[hascap r]} { menulink /ticket Tickets } if {[hascap j]} { menulink /wiki Wiki } if {[hascap s]} { menulink /setup Admin } elseif {[hascap a]} { menulink /setup_ulist Users } </th1></div> |
Added skins/eagle/README.md.
> > > > | 1 2 3 4 | For this skin to look exactly as it was intended to, the **white-foreground** setting should be enabled. This skin was contributed by Joe Mistachkin. |
Added skins/eagle/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | /* General settings for the entire page */ body { margin: 0ex 1ex; padding: 0px; background-color: #485D7B; font-family: sans-serif; color: white; -moz-text-size-adjust: none; -webkit-text-size-adjust: none; -mx-text-size-adjust: none; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: table-cell; text-align: center; vertical-align: bottom; font-weight: bold; color: white; padding: 5 0 5 0em; white-space: nowrap; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 2em; font-weight: bold; text-align: left; padding: 0 0 0 1em; color: white; vertical-align: bottom; width: 100%; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; text-align: right; vertical-align: bottom; color: white; font-size: 0.8em; font-weight: bold; min-width: 200px; white-space: nowrap; } /* The header across the top of the page */ div.header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ div.mainmenu { padding: 5px 10px 5px 10px; font-size: 0.9em; font-weight: bold; text-align: center; letter-spacing: 1px; background-color: #76869D; border-top-left-radius: 8px; border-top-right-radius: 8px; color: white; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu, div.sectionmenu { padding: 3px 10px 3px 0px; font-size: 0.9em; font-weight: bold; text-align: center; background-color: #485D7B; color: white; } div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { text-decoration: underline; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { padding: 0ex 1ex 0ex 2ex; } /* Some pages have section dividers */ div.section { margin-bottom: 0px; margin-top: 1em; padding: 1px 1px 1px 1px; font-size: 1.2em; font-weight: bold; background-color: #485D7B; color: white; white-space: nowrap; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #9DB0CC; color: white; border: 2px white solid; font-size: 1em; font-weight: normal; padding: .25em; margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ div.footer { clear: both; font-size: 0.8em; margin-top: 12px; padding: 5px 10px 5px 10px; text-align: right; background-color: #485D7B; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; color: white; } /* Hyperlink colors in the footer */ a { color: white; } a:link { color: white; } a:visited { color: white; } a:hover { color: #9DB0CC; } /* verbatim blocks */ pre.verbatim { background-color: #485D7B; color: white; padding: 0.5em; white-space: pre-wrap; } /* The label/value pairs on (for example) the ci page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } /* The nomenclature sidebox for branches,.. */ div.sidebox { float: right; background-color: #485D7B; border-width: medium; border-style: double; margin: 10px; } /* the format for the timeline data table */ table.timelineTable { cellspacing: 0; border: 0; cellpadding: 0; font-family: "courier new"; border-collapse: collapse; } tr.timelineSelected { background-color: #7EA2D9; } /* Side-by-side diff */ table.sbsdiff { background-color: #485D7B; font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace; font-size: 8pt; border-collapse:collapse; white-space: pre; width: 98%; border: 1px #000 dashed; margin-left: auto; margin-right: auto; } /* format for the layout table, used for the captcha display */ table.captcha { margin: auto; padding: 10px; border-width: 4px; border-style: double; border-color: white; } /* format for the user list table on the user setup page */ table.usetupUserList { outline-style: double; outline-width: 1px; outline-color: white; padding: 10px; } /* color for capabilities, inherited by reader */ span.ueditInheritReader { color: white; } /* format for values on ticket display page */ td.tktDspValue { text-align: left; vertical-align: top; background-color: #485D7B; } /* format for example table cells on the report edit page */ td.rpteditex { border-width: thin; border-color: white; border-style: solid; } /* List of files in a timeline */ ul.filelist { margin-top: 3px; line-height: 100%; } /* side-by-side diff display */ div.sbsdiff { font-family: monospace; font-size: smaller; white-space: pre; } /* context diff display */ div.udiff { font-family: monospace; white-space: pre; } /* changes in a diff */ span.diffchng { background-color: rgb(170, 170, 140); } /* added code in a diff */ span.diffadd { background-color: rgb(100, 200, 100); } /* deleted in a diff */ span.diffrm { background-color: rgb(230, 110, 110); } /* suppressed lines in a diff */ span.diffhr { display: inline-block; margin: .5em 0 1em; color: rgb(150, 150, 140); } /* line numbers in a diff */ span.diffln { color: white; } #canvas { background-color: #485D7B; } .fileage tr:hover { background-color: #7EA2D9; } .fileage td { font-family: "courier new"; } div.filetreeline:hover { background-color: #7EA2D9; } div.selectedText { background-color: #7EA2D9; } .statistics-report-graph-line { background-color: #7EA2D9; } |
Added skins/eagle/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 1 |
Added skins/eagle/footer.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <div class="footer"> <th1> proc getTclVersion {} { if {[catch {tclEval info patchlevel} tclVersion] == 0} { return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" } return "" } proc getVersion { version } { set length [string length $version] return [string range $version 1 [expr {$length - 2}]] } set version [getVersion $manifest_version] set tclVersion [getTclVersion] set fossilUrl https://www.fossil-scm.org set fossilDate [string range $manifest_date 0 9]T[string range $manifest_date 11 end] </th1> This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by <a href="$fossilUrl/">Fossil</a> version $release_version $tclVersion <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> <a href="$fossilUrl/index.html/timeline?c=$fossilDate&y=ci">$manifest_date</a> </div> </body></html> |
Added skins/eagle/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" /> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" /> </head> <body> <div class="header"> <div class="logo"> <th1> ## ## NOTE: The purpose of this procedure is to take the base URL of the ## Fossil project and return the root of the entire web site using ## the same URI scheme as the base URL (e.g. http or https). ## proc getLogoUrl { baseurl } { set idx(first) [string first // $baseurl] if {$idx(first) != -1} { ## ## NOTE: Skip second slash. ## set idx(first+1) [expr {$idx(first) + 2}] ## ## NOTE: (part 1) The [string first] command does NOT actually ## support the optional startIndex argument as specified ## in the TH1 support manual; therefore, we fake it by ## using the [string range] command and then adding the ## necessary offset to the resulting index manually ## (below). In Tcl, we could use the following instead: ## ## set idx(next) [string first / $baseurl $idx(first+1)] ## set idx(nextRange) [string range $baseurl $idx(first+1) end] set idx(next) [string first / $idx(nextRange)] if {$idx(next) != -1} { ## ## NOTE: (part 2) Add the necessary offset to the result of ## the search for the next slash (i.e. the one after ## the initial search for the two slashes). ## set idx(next) [expr {$idx(next) + $idx(first+1)}] ## ## NOTE: Back up one character from the next slash. ## set idx(next-1) [expr {$idx(next) - 1}] ## ## NOTE: Extract the URI scheme and host from the base URL. ## set scheme [string range $baseurl 0 $idx(first)] set host [string range $baseurl $idx(first+1) $idx(next-1)] ## ## NOTE: Try to stay in SSL mode if we are there now. ## if {[string compare $scheme http:/] == 0} { set scheme http:// } else { set scheme https:// } set logourl $scheme$host/ } else { set logourl $baseurl } } else { set logourl $baseurl } return $logourl } set logourl [getLogoUrl $baseurl] </th1> <a href="$logourl"> <img src="$logo_image_url" border="0" alt="$project_name"> </a> </div> <div class="title">$<title></div> <div class="status"><nobr><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></nobr><small><div id="clock"></div></small></div> </div> <script> function updateClock(){ var e = document.getElementById("clock"); if(e){ var d = new Date(); function f(n) { return n < 10 ? '0' + n : n; } e.innerHTML = d.getUTCFullYear()+ '-' + f(d.getUTCMonth() + 1) + '-' + f(d.getUTCDate()) + ' ' + f(d.getUTCHours()) + ':' + f(d.getUTCMinutes()); setTimeout("updateClock();",(60-d.getUTCSeconds())*1000); } } updateClock(); </script> <div class="mainmenu"> <th1> proc menulink {url name} { upvar home home html "<a href='$home$url'>$name</a>\n" } menulink $index_page Home menulink /help Help if {[anycap jor]} { menulink /timeline Timeline } if {[anoncap oh]} { menulink /dir?ci=tip Files } if {[anoncap o]} { menulink /brlist Branches menulink /taglist Tags } if {[anoncap r]} { menulink /ticket Tickets } if {[anoncap j]} { menulink /wiki Wiki } if {[hascap s]} { menulink /setup Admin } elseif {[hascap a]} { menulink /setup_ulist Users } if {[info exists login]} { menulink /login Logout } else { menulink /login Login } </th1></div> |
Added skins/enhanced1/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | /* General settings for the entire page */ body { margin: 0ex 1ex; padding: 0px; background-color: white; font-family: sans-serif; -moz-text-size-adjust: none; -webkit-text-size-adjust: none; -mx-text-size-adjust: none; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: table-cell; text-align: center; vertical-align: bottom; font-weight: bold; color: #558195; min-width: 200px; white-space: nowrap; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 2em; font-weight: bold; text-align: center; padding: 0 0 0 1em; color: #558195; vertical-align: bottom; width: 100%; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; text-align: right; vertical-align: bottom; color: #558195; font-size: 0.8em; font-weight: bold; min-width: 200px; white-space: nowrap; } /* The header across the top of the page */ div.header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ div.mainmenu { padding: 5px 10px 5px 10px; font-size: 0.9em; font-weight: bold; text-align: center; letter-spacing: 1px; background-color: #558195; border-top-left-radius: 8px; border-top-right-radius: 8px; color: white; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu, div.sectionmenu { padding: 3px 10px 3px 0px; font-size: 0.9em; text-align: center; background-color: #456878; color: white; } div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { color: #558195; background-color: white; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { padding: 0ex 1ex 1ex 1ex; border: solid #aaa; border-width: 1px; } /* Some pages have section dividers */ div.section { margin-bottom: 0px; margin-top: 1em; padding: 1px 1px 1px 1px; font-size: 1.2em; font-weight: bold; background-color: #558195; color: white; white-space: nowrap; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #a1c4d4; border: 2px #558195 solid; font-size: 1em; font-weight: normal; padding: .25em; margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ div.footer { clear: both; font-size: 0.8em; padding: 5px 10px 5px 10px; text-align: right; background-color: #558195; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; color: white; } /* Hyperlink colors in the footer */ div.footer a { color: white; } div.footer a:link { color: white; } div.footer a:visited { color: white; } div.footer a:hover { background-color: white; color: #558195; } /* verbatim blocks */ pre.verbatim { background-color: #f5f5f5; padding: 0.5em; white-space: pre-wrap; } /* The label/value pairs on (for example) the ci page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } |
Added skins/enhanced1/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/enhanced1/footer.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <div class="footer"> <th1> proc getTclVersion {} { if {[catch {tclEval info patchlevel} tclVersion] == 0} { return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" } return "" } proc getVersion { version } { set length [string length $version] return [string range $version 1 [expr {$length - 2}]] } set version [getVersion $manifest_version] set tclVersion [getTclVersion] set fossilUrl https://www.fossil-scm.org set fossilDate [string range $manifest_date 0 9]T[string range $manifest_date 11 end] </th1> This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by <a href="$fossilUrl/">Fossil</a> version $release_version $tclVersion <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> <a href="$fossilUrl/index.html/timeline?c=$fossilDate&y=ci">$manifest_date</a> </div> </body></html> |
Added skins/enhanced1/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" /> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" /> </head> <body> <div class="header"> <div class="logo"> <th1> ## ## NOTE: The purpose of this procedure is to take the base URL of the ## Fossil project and return the root of the entire web site using ## the same URI scheme as the base URL (e.g. http or https). ## proc getLogoUrl { baseurl } { set idx(first) [string first // $baseurl] if {$idx(first) != -1} { ## ## NOTE: Skip second slash. ## set idx(first+1) [expr {$idx(first) + 2}] ## ## NOTE: (part 1) The [string first] command does NOT actually ## support the optional startIndex argument as specified ## in the TH1 support manual; therefore, we fake it by ## using the [string range] command and then adding the ## necessary offset to the resulting index manually ## (below). In Tcl, we could use the following instead: ## ## set idx(next) [string first / $baseurl $idx(first+1)] ## set idx(nextRange) [string range $baseurl $idx(first+1) end] set idx(next) [string first / $idx(nextRange)] if {$idx(next) != -1} { ## ## NOTE: (part 2) Add the necessary offset to the result of ## the search for the next slash (i.e. the one after ## the initial search for the two slashes). ## set idx(next) [expr {$idx(next) + $idx(first+1)}] ## ## NOTE: Back up one character from the next slash. ## set idx(next-1) [expr {$idx(next) - 1}] ## ## NOTE: Extract the URI scheme and host from the base URL. ## set scheme [string range $baseurl 0 $idx(first)] set host [string range $baseurl $idx(first+1) $idx(next-1)] ## ## NOTE: Try to stay in SSL mode if we are there now. ## if {[string compare $scheme http:/] == 0} { set scheme http:// } else { set scheme https:// } set logourl $scheme$host/ } else { set logourl $baseurl } } else { set logourl $baseurl } return $logourl } set logourl [getLogoUrl $baseurl] </th1> <a href="$logourl"> <img src="$logo_image_url" border="0" alt="$project_name"> </a> </div> <div class="title">$<title></div> <div class="status"><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></nobr><small><div id="clock"></div></small></div> </div> <script> function updateClock(){ var e = document.getElementById("clock"); if(e){ var d = new Date(); function f(n) { return n < 10 ? '0' + n : n; } e.innerHTML = d.getUTCFullYear()+ '-' + f(d.getUTCMonth() + 1) + '-' + f(d.getUTCDate()) + ' ' + f(d.getUTCHours()) + ':' + f(d.getUTCMinutes()); setTimeout("updateClock();",(60-d.getUTCSeconds())*1000); } } updateClock(); </script> <div class="mainmenu"> <th1> proc menulink {url name} { upvar home home html "<a href='$home$url'>$name</a>\n" } menulink $index_page Home menulink /help Help if {[anycap jor]} { menulink /timeline Timeline } if {[anoncap oh]} { menulink /dir?ci=tip Files } if {[anoncap o]} { menulink /brlist Branches menulink /taglist Tags } if {[anoncap r]} { menulink /ticket Tickets } if {[anoncap j]} { menulink /wiki Wiki } if {[hascap s]} { menulink /setup Admin } elseif {[hascap a]} { menulink /setup_ulist Users } if {[info exists login]} { menulink /login Logout } else { menulink /login Login } </th1></div> |
Added skins/khaki/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | /* General settings for the entire page */ body { margin: 0ex 0ex; padding: 0px; background-color: #fef3bc; font-family: sans-serif; -moz-text-size-adjust: none; -webkit-text-size-adjust: none; -mx-text-size-adjust: none; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: inline; text-align: center; vertical-align: bottom; font-weight: bold; font-size: 2.5em; color: #a09048; white-space: nowrap; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 2em; font-weight: bold; text-align: left; padding: 0 0 0 5px; color: #a09048; vertical-align: bottom; width: 100%; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; text-align: right; vertical-align: bottom; color: #a09048; padding: 5px 5px 0 0; font-size: 0.8em; font-weight: bold; white-space: nowrap; } /* The header across the top of the page */ div.header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ div.mainmenu { padding: 5px 10px 5px 10px; font-size: 0.9em; font-weight: bold; text-align: center; letter-spacing: 1px; background-color: #a09048; color: black; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu, div.sectionmenu { padding: 3px 10px 3px 0px; font-size: 0.9em; text-align: center; background-color: #c0af58; color: white; } div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { color: #a09048; background-color: white; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { padding: 1ex 5px; } div.content a { color: #706532; } div.content a:link { color: #706532; } div.content a:visited { color: #704032; } div.content a:hover { background-color: white; color: #706532; } /* Some pages have section dividers */ div.section { margin-bottom: 0px; margin-top: 1em; padding: 3px 3px 0 3px; font-size: 1.2em; font-weight: bold; background-color: #a09048; color: white; white-space: nowrap; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #e1d498; border: 2px #a09048 solid; font-size: 1em; font-weight: normal; padding: .25em; margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ div.footer { font-size: 0.8em; margin-top: 12px; padding: 5px 10px 5px 10px; text-align: right; background-color: #a09048; color: white; } /* Hyperlink colors */ div.footer a { color: white; } div.footer a:link { color: white; } div.footer a:visited { color: white; } div.footer a:hover { background-color: white; color: #558195; } /* <verbatim> blocks */ pre.verbatim { background-color: #f5f5f5; padding: 0.5em; white-space: pre-wrap; } /* The label/value pairs on (for example) the ci page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } |
Added skins/khaki/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/khaki/footer.txt.
> > > > | 1 2 3 4 | <div class="footer"> Fossil version $manifest_version $manifest_date </div> </body></html> |
Added skins/khaki/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss"> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen"> </head> <body> <div class="header"> <div class="title">$<title></div> <div class="status"> <div class="logo">$<project_name></div><br/> <th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></div> </div> <div class="mainmenu"> <th1> html "<a href='$home$index_page'>Home</a>\n" if {[anycap jor]} { html "<a href='$home/timeline'>Timeline</a>\n" } if {[anoncap oh]} { html "<a href='$home/tree?ci=tip'>Files</a>\n" } if {[anoncap o]} { html "<a href='$home/brlist'>Branches</a>\n" html "<a href='$home/taglist'>Tags</a>\n" } if {[anoncap r]} { html "<a href='$home/ticket'>Tickets</a>\n" } if {[anoncap j]} { html "<a href='$home/wiki'>Wiki</a>\n" } if {[hascap s]} { html "<a href='$home/setup'>Admin</a>\n" } elseif {[hascap a]} { html "<a href='$home/setup_ulist'>Users</a>\n" } if {[info exists login]} { html "<a href='$home/login'>Logout</a>\n" } else { html "<a href='$home/login'>Login</a>\n" } </th1></div> |
Added skins/original/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | /* General settings for the entire page */ body { margin: 0ex 1ex; padding: 0px; background-color: white; font-family: sans-serif; -moz-text-size-adjust: none; -webkit-text-size-adjust: none; -mx-text-size-adjust: none; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: table-cell; text-align: center; vertical-align: bottom; font-weight: bold; color: #558195; min-width: 200px; white-space: nowrap; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 2em; font-weight: bold; text-align: center; padding: 0 0 0 1em; color: #558195; vertical-align: bottom; width: 100%; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; text-align: right; vertical-align: bottom; color: #558195; font-size: 0.8em; font-weight: bold; min-width: 200px; white-space: nowrap; } /* The header across the top of the page */ div.header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ div.mainmenu { padding: 5px 10px 5px 10px; font-size: 0.9em; font-weight: bold; text-align: center; letter-spacing: 1px; background-color: #558195; border-top-left-radius: 8px; border-top-right-radius: 8px; color: white; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu, div.sectionmenu { padding: 3px 10px 3px 0px; font-size: 0.9em; text-align: center; background-color: #456878; color: white; } div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { color: #558195; background-color: white; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { padding: 0ex 1ex 1ex 1ex; border: solid #aaa; border-width: 1px; } /* Some pages have section dividers */ div.section { margin-bottom: 0px; margin-top: 1em; padding: 1px 1px 1px 1px; font-size: 1.2em; font-weight: bold; background-color: #558195; color: white; white-space: nowrap; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #a1c4d4; border: 2px #558195 solid; font-size: 1em; font-weight: normal; padding: .25em; margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ div.footer { clear: both; font-size: 0.8em; padding: 5px 10px 5px 10px; text-align: right; background-color: #558195; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; color: white; } /* Hyperlink colors in the footer */ div.footer a { color: white; } div.footer a:link { color: white; } div.footer a:visited { color: white; } div.footer a:hover { background-color: white; color: #558195; } /* verbatim blocks */ pre.verbatim { background-color: #f5f5f5; padding: 0.5em; white-space: pre-wrap; } /* The label/value pairs on (for example) the ci page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } |
Added skins/original/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/original/footer.txt.
> > > > > > | 1 2 3 4 5 6 | <div class="footer"> This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by Fossil version $manifest_version $manifest_date </div> </body></html> |
Added skins/original/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" /> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" /> </head> <body> <div class="header"> <div class="logo"> <img src="$logo_image_url" alt="logo" /> </div> <div class="title"><small>$<project_name></small><br />$<title></div> <div class="status"><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></div> </div> <div class="mainmenu"> <th1> html "<a href='$home$index_page'>Home</a>\n" if {[anycap jor]} { html "<a href='$home/timeline'>Timeline</a>\n" } if {[anoncap oh]} { html "<a href='$home/tree?ci=tip'>Files</a>\n" } if {[anoncap o]} { html "<a href='$home/brlist'>Branches</a>\n" html "<a href='$home/taglist'>Tags</a>\n" } if {[anoncap r]} { html "<a href='$home/ticket'>Tickets</a>\n" } if {[anoncap j]} { html "<a href='$home/wiki'>Wiki</a>\n" } if {[hascap s]} { html "<a href='$home/setup'>Admin</a>\n" } elseif {[hascap a]} { html "<a href='$home/setup_ulist'>Users</a>\n" } if {[info exists login]} { html "<a href='$home/login'>Logout</a>\n" } else { html "<a href='$home/login'>Login</a>\n" } </th1></div> |
Added skins/plain_gray/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | /* General settings for the entire page */ body { margin: 0ex 1ex; padding: 0px; background-color: white; font-family: sans-serif; -moz-text-size-adjust: none; -webkit-text-size-adjust: none; -mx-text-size-adjust: none; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: table-row; text-align: center; /* vertical-align: bottom;*/ font-size: 2em; font-weight: bold; background-color: #707070; color: #ffffff; min-width: 200px; white-space: nowrap; } /* The page title centered at the top of each page */ div.title { display: table-cell; font-size: 1.5em; font-weight: bold; text-align: center; padding: 0 0 0 10px; color: #404040; vertical-align: bottom; width: 100%; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; text-align: right; vertical-align: bottom; color: #404040; font-size: 0.8em; font-weight: bold; min-width: 200px; white-space: nowrap; } /* The header across the top of the page */ div.header { display: table; width: 100%; } /* The main menu bar that appears at the top of the page beneath ** the header */ div.mainmenu { padding: 5px 10px 5px 10px; font-size: 0.9em; font-weight: bold; text-align: center; letter-spacing: 1px; background-color: #404040; color: white; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu, div.sectionmenu { padding: 3px 10px 3px 0px; font-size: 0.9em; text-align: center; background-color: #606060; color: white; } div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { color: #404040; background-color: white; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { padding: 0ex 0ex 0ex 0ex; } /* Hyperlink colors */ div.content a { color: #604000; } div.content a:link { color: #604000;} div.content a:visited { color: #600000; } /* <verbatim> blocks */ pre.verbatim { background-color: #ffffff; padding: 0.5em; white-space: pre-wrap; } /* Some pages have section dividers */ div.section { margin-bottom: 0px; margin-top: 1em; padding: 1px 1px 1px 1px; font-size: 1.2em; font-weight: bold; background-color: #404040; color: white; white-space: nowrap; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { background: #a0a0a0; border: 2px #505050 solid; font-size: 1em; font-weight: normal; padding: .25em; margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ div.footer { font-size: 0.8em; margin-top: 12px; padding: 5px 10px 5px 10px; text-align: right; background-color: #404040; color: white; } /* The label/value pairs on (for example) the vinfo page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } |
Added skins/plain_gray/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/plain_gray/footer.txt.
> > > > | 1 2 3 4 | <div class="footer"> Fossil version $manifest_version $manifest_date </div> </body></html> |
Added skins/plain_gray/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss"> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen"> </head> <body> <div class="header"> <div class="title"><small>$<project_name></small><br />$<title></div> <div class="status"><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></div> </div> <div class="mainmenu"> <th1> html "<a href='$home$index_page'>Home</a>\n" if {[anycap jor]} { html "<a href='$home/timeline'>Timeline</a>\n" } if {[anoncap oh]} { html "<a href='$home/tree?ci=tip'>Files</a>\n" } if {[anoncap o]} { html "<a href='$home/brlist'>Branches</a>\n" html "<a href='$home/taglist'>Tags</a>\n" } if {[anoncap r]} { html "<a href='$home/ticket'>Tickets</a>\n" } if {[anoncap j]} { html "<a href='$home/wiki'>Wiki</a>\n" } if {[hascap s]} { html "<a href='$home/setup'>Admin</a>\n" } elseif {[hascap a]} { html "<a href='$home/setup_ulist'>Users</a>\n" } if {[info exists login]} { html "<a href='$home/login'>Logout</a>\n" } else { html "<a href='$home/login'>Login</a>\n" } </th1></div> |
Added skins/rounded1/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | /* General settings for the entire page */ html { min-height: 100%; } body { margin: 0ex 1ex; padding: 0px; background-color: white; color: #333; font-family: Verdana, sans-serif; font-size: 0.8em; -moz-text-size-adjust: none; -webkit-text-size-adjust: none; -mx-text-size-adjust: none; } /* The project logo in the upper left-hand corner of each page */ div.logo { display: table-cell; text-align: right; vertical-align: bottom; font-weight: normal; white-space: nowrap; } /* Widths */ div.header, div.mainmenu, div.submenu, div.content, div.footer { max-width: 900px; margin: auto; padding: 3px 20px 3px 20px; clear: both; } /* The page title at the top of each page */ div.title { display: table-cell; padding-left: 10px; font-size: 2em; margin: 10px 0 10px -20px; vertical-align: bottom; text-align: left; width: 80%; font-family: Verdana, sans-serif; font-weight: bold; color: #558195; text-shadow: 0px 2px 2px #999999; } /* The login status message in the top right-hand corner */ div.status { display: table-cell; text-align: right; vertical-align: bottom; color: #333; margin-right: -20px; white-space: nowrap; } /* The main menu bar that appears at the top of the page beneath ** the header */ div.mainmenu { text-align: center; color: white; border-top-left-radius: 5px; border-top-right-radius: 5px; vertical-align: middle; padding-top: 8px; padding-bottom: 8px; background-color: #446979; box-shadow: 0px 3px 4px #333333; } /* The submenu bar that *sometimes* appears below the main menu */ div.submenu { padding-top:10px; padding-bottom:0; text-align: right; color: #000; background-color: #fff; height: 1.5em; vertical-align:middle; box-shadow: 0px 3px 4px #999; } div.mainmenu a, div.mainmenu a:visited { padding: 3px 10px 3px 10px; color: white; text-decoration: none; } div.submenu a, div.submenu a:visited, a.button, div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { padding: 2px 8px; color: #000; font-family: Arial; text-decoration: none; margin:auto; border-radius: 5px; background-color: #e0e0e0; text-shadow: 0px -1px 0px #eee; border: 1px solid #000; } div.mainmenu a:hover { color: #000; background-color: white; } div.submenu a:hover, div.sectionmenu>a.button:hover { background-color: #c0c0c0; } /* All page content from the bottom of the menu or submenu down to ** the footer */ div.content { background-color: #fff; box-shadow: 0px 3px 4px #999; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; padding-bottom: 1em; min-height:40%; } /* Some pages have section dividers */ div.section { margin-bottom: 0.5em; margin-top: 1em; margin-right: auto; padding: 1px 1px 1px 1px; font-size: 1.2em; font-weight: bold; text-align: center; color: white; border-radius: 5px; background-color: #446979; box-shadow: 0px 3px 4px #333333; white-space: nowrap; } /* The "Date" that occurs on the left hand side of timelines */ div.divider { font-size: 1.2em; font-family: Georgia, serif; font-weight: bold; margin-top: 1em; white-space: nowrap; } /* The footer at the very bottom of the page */ div.footer { font-size: 0.9em; text-align: right; margin-bottom: 1em; color: #666; } /* Hyperlink colors in the footer */ div.footer a { color: white; } div.footer a:link { color: white; } div.footer a:visited { color: white; } div.footer a:hover { background-color: white; color: #558195; } /* <verbatim> blocks */ pre.verbatim, blockquote pre { font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace; background-color: #f3f3f3; padding: 0.5em; white-space: pre-wrap; } blockquote pre { border: 1px #000 dashed; } /* The label/value pairs on (for example) the ci page */ table.label-value th { vertical-align: top; text-align: right; padding: 0.2ex 2ex; } table.report tr th { padding: 3px 5px; text-transform: capitalize; cursor: pointer; } table.report tr td { padding: 3px 5px; } textarea { font-size: 1em; } .fullsize-text { font-size: 1.25em; } |
Added skins/rounded1/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/rounded1/footer.txt.
> > > > | 1 2 3 4 | <div class="footer"> Fossil version $manifest_version $manifest_date </div> </body></html> |
Added skins/rounded1/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss"> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen"> </head> <body> <div class="header"> <div class="logo"> <img src="$logo_image_url" alt="logo"> <br />$<project_name> </div> <div class="title">$<title></div> <div class="status"><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></div> </div> <div class="mainmenu"> <th1> html "<a href='$home$index_page'>Home</a>\n" if {[anycap jor]} { html "<a href='$home/timeline'>Timeline</a>\n" } if {[anoncap oh]} { html "<a href='$home/tree?ci=tip'>Files</a>\n" } if {[anoncap o]} { html "<a href='$home/brlist'>Branches</a>\n" html "<a href='$home/taglist'>Tags</a>\n" } if {[anoncap r]} { html "<a href='$home/ticket'>Tickets</a>\n" } if {[anoncap j]} { html "<a href='$home/wiki'>Wiki</a>\n" } if {[hascap s]} { html "<a href='$home/setup'>Admin</a>\n" } elseif {[hascap a]} { html "<a href='$home/setup_ulist'>Users</a>\n" } if {[info exists login]} { html "<a href='$home/login'>Logout</a>\n" } else { html "<a href='$home/login'>Login</a>\n" } </th1></div> |
Added skins/xekri/README.md.
> > | 1 2 | "xekri" is a Lojban word that means "extermely dark-colored". This skin was contributed by Andrew Moore. |
Added skins/xekri/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 | /****************************************************************************** * Xekri * * To adjust the width of the contents for this skin, look for the "max-width" * property and change its value. (It's in the "Main Area" section) The value * determines how much of the browser window to use. Some like 100%, so that * the entire window is used. Others prefer 80%, which makes the contents * easier to read for them. */ /************************************** * General HTML */ html { background-color: #333; color: #eee; font-family: Monospace; font-size: 1em; min-height: 100%; } body { margin: 0; padding: 0; -moz-text-size-adjust: none; -ms-text-size-adjust: none; -webkit-text-size-adjust: none; } a { color: #07e; } a:hover { font-weight: bold; } blockquote pre { border: 1px dashed #ee0; } blockquote pre, pre.verbatim { background-color: #000; border-radius: 0.75rem; padding: 0.5rem; white-space: pre-wrap; } input[type="password"], input[type="text"], textarea { background-color: #111; color: #fff; font-size: 1rem; } h1 { font-size: 2rem; } h2 { font-size: 1.5rem; } h3 { font-size: 1.25rem; } span[style^=background-color] { color: #000; } td[style^=background-color] { color: #000; } /************************************** * Main Area */ div.header, div.mainmenu, div.submenu, div.content, div.footer { clear: both; margin: 0 auto; max-width: 90%; padding: 0.25rem 1rem; } /************************************** * Main Area: Header */ div.header { margin: 0.5rem auto 0 auto; } div.logo img { float: left; padding: 0; box-shadow: 3px 3px 1px #000; margin: 0 6px 6px 0; } div.logo br { display: none; } div.logo nobr { color: #eee; font-size: 1.2rem; font-weight: bold; padding: 0; text-shadow: 3px 3px 1px #000; vertical-align: top; white-space: nowrap; } div.title { color: #07e; font-family: Verdana, sans-serif; font-weight: bold; font-size: 2.5rem; padding: 0.5rem; text-align: center; text-shadow: 3px 3px 1px #000; } div.status { color: #ee0; font-size: 1rem; padding: 0.25rem; text-align: right; text-shadow: 2px 2px 1px #000; } /************************************** * Main Area: Global Menu */ div.mainmenu, div.submenu { background-color: #080; border-radius: 1rem 1rem 0 0; box-shadow: 3px 4px 1px #000; color: #000; font-weight: bold; font-size: 1.1rem; text-align: center; } div.mainmenu { padding-top: 0.33rem; padding-bottom: 0.25rem; } div.submenu { border-top: 1px solid #0a0; border-radius: 0; display: block; } div.mainmenu a, div.submenu a { color: #000; padding: 0 0.75rem; text-decoration: none; } div.mainmenu a:hover, div.submenu a:hover { color: #fff; text-shadow: 0px 0px 6px #0f0; } div.submenu * { margin: 0 0.5rem; vertical-align: middle; } div.submenu select, div.submenu input { background-color: #222; border: 1px inset #080; color: #eee; cursor: pointer; font-size: 0.9rem; } div.submenu select { height: 1.75rem; } /************************************** * Main Area: Content */ div.content { background-color: #222; border-radius: 0 0 1rem 1rem; box-shadow: 3px 3px 1px #000; min-height:40%; padding-bottom: 1rem; padding-top: 0.5rem; } div.content table[bgcolor="white"] { color: #000; } /************************************** * Main Area: Footer */ div.footer { color: #ee0; font-size: 0.75rem; padding: 0; text-align: right; width: 75%; } div.footer div { background-color: #222; box-shadow: 3px 3px 1px #000; border-radius: 0 0 1rem 1rem; margin: 0 0 10px 0; padding: 0.5rem 0.75rem; } div.footer div.page-time { float: left; } div.footer div.fossil-info { float: right; } div.footer a, div.footer a:link, div.footer a:visited { color: #ee0; } div.footer a:hover { color: #fff; text-shadow: 0px 0px 6px #ee0; } /************************************** * Check-in */ table.label-value th { vertical-align: top; text-align: right; padding: 0.1rem 1rem; } /************************************** * Diffs */ /* Code Added */ span.diffadd { background-color: #7f7; color: #000; } /* Code Changed */ span.diffchng { background-color: #77f; color: #000; } /* Code Deleted */ span.diffrm { background-color: #f77; color: #000; } /************************************** * Diffs : Side-By-Side */ /* display (column-based) */ table.sbsdiffcols { border-spacing: 0; font-size: 0.85rem; width: 90%; } table.sbsdiffcols pre { border: 0; margin: 0; padding: 0; } table.sbsdiffcols td { padding: 0; vertical-align: top; } /* line number column */ div.difflncol { color: #ee0; padding-right: 0.75em; text-align: right; } /* diff text column */ div.difftxtcol { background-color: #111; overflow-x: auto; width: 45em; } /* suppressed lines */ span.diffhr { display: inline-block; margin-bottom: 0.75em; color: #ff0; } /* diff marker column */ div.diffmkrcol { padding: 0 0.5em; } /************************************** * Diffs : Unified */ pre.udiff { background-color: #111; } /* line numbers */ span.diffln { background-color: #222; color: #ee0; } /************************************** * File List : Flat */ table.browser { width: 100%; border: 0; } td.browser { width: 24%; vertical-align: top; } ul.browser { margin: 0.5rem; padding: 0.5rem; white-space: nowrap; } ul.browser li.dir { font-style: italic } /************************************** * File List : Age */ .fileage tr:hover td { background-color: #225; } /************************************** * File List : Tree */ .filetree { line-height: 1.5; margin: 1rem 0; } /* list */ .filetree ul { list-style: none; margin: 0; padding: 0; } /* collapsed list */ .filetree ul.collapsed { display: none; } /* lists below the root */ .filetree ul ul { margin: 0 0 0 21px; position: relative; } /* lists items */ .filetree li { margin: 0; padding: 0; position: relative; } /* node lines */ .filetree li li:before { border-bottom: 2px solid #000; border-left: 2px solid #000; content: ''; height: 1.5rem; left: -14px; position: absolute; top: -0.8rem; width: 14px; } /* directory lines */ .filetree li > ul:before { border-left: 2px solid #000; bottom: 0; content: ''; left: -35px; position: absolute; top: -1.5rem; } /* hide lines for last-child directories */ .filetree li.last > ul:before { display: none; } .filetree a { background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP\/\/\/yEhIf\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmgOUvoaqDSCxrEEfF14GqFXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw==); background-position: center left; background-repeat: no-repeat; display: inline-block; min-height: 16px; padding-left: 21px; position: relative; z-index: 1; } .filetree .dir > a { background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIiIv\/\/\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXpo+jUs6b5Z/K4siDu5RPUFADs=); font-style: italic } .filetreeline:hover { color: #000; font-weight: bold; } .filetreeline .filetreeage { padding-right: 0.5rem; } /************************************** * Logout */ span.loginError { color: #f00; } table.login_out { margin: 10px; text-align: left; } td.login_out_label { text-align: center; } div.captcha { padding: 1rem; text-align: center; } table.captcha { background-color: #111; border-color: #111; border-style: inset; border-width: 2px; margin: auto; padding: 0.5rem; } table.captcha pre { color: #ee0; } /************************************** * Statistics Reports */ .statistics-report-graph-line { background-color: #22e; } .statistics-report-table-events th { padding: 0 1rem; } .statistics-report-table-events td { padding: 0.1rem 1rem; } .statistics-report-row-year { color: #ee0; text-align: left; } .statistics-report-week-number-label { font-size: 0.8rem; text-align: right; } .statistics-report-week-of-year-list { font-size: 0.8rem; } /************************************** * Search */ .searchResult .snippet mark { color: #ee0; } /************************************** * Sections */ div.section, div.sectionmenu { color: #2ee; background-color: #22c; border-radius: 0 3rem; box-shadow: 2px 2px #000; display: flex; font-size: 1.1rem; font-weight: bold; justify-content: space-around; margin: 1.2rem auto 0.75rem auto; padding: 0.2rem; text-align: center; } div.sectionmenu { border-radius: 0 0 3rem 3rem; margin-top: -0.75rem; width: 75%; } div.sectionmenu > a:link, div.sectionmenu > a:visited { color: #000; text-decoration: none; } div.sectionmenu > a:hover { color: #eee; text-shadow: 0px 0px 6px #eee; } /************************************** * Sidebox */ div.sidebox { background-color: #333; border-radius: 0.5rem; box-shadow: 3px 3px 1px #000; float: right; margin: 1rem 0.5rem; padding: 0.5rem; } div.sidebox ol { margin: 0 0 0.5rem 2.5rem; padding: 0 0; } div.sidebox ol li { margin-top: 0.75rem; } div.sideboxTitle { background-color: #ee0; border-radius: 0.5rem 0.5rem 0 0; color: #000; font-weight: bold; margin: -0.5rem -0.5rem 0 -0.5rem; padding: 0.25rem; text-align: center; } div.sideboxDescribed { display: inline; } /* --- Untested : Begin --- */ /* The defined element in sideboxes for branches,.. */ span.disabled { color: #f00; } /* --- Untested : End --- */ /************************************** * Tag */ /* --- Untested : Begin --- */ /* the format for the tag links */ a.tagLink { } /* the format for the tag display(no history permission!) */ span.tagDsp { font-weight: bold; } /* the format for fixed/canceled tags,.. */ span.infoTagCancelled { font-weight: bold; text-decoration: line-through; } /* --- Untested : End --- */ /************************************** * Ticket */ table.report { color: #000; border: 1px solid #999; border-collapse: collapse; margin: 1rem 0; } table.report tr th { color: #eee; padding: 3px 5px; text-transform : capitalize; } table.report tr td { padding: 3px 5px; } /* example ticket colors */ table.rpteditex { border-collapse: collapse; border-spacing: 0; color: #000; float: right; margin: 0; padding: 0; text-align: center; width: 125px; } td.rpteditex { border-color: #000; border-style: solid; border-width: thin; } #reportTable { } /* format for labels on ticket display page */ td.tktDspLabel { text-align: right; } /* format for values on ticket display page */ td.tktDspValue { background-color: #111; text-align: left; vertical-align: top; } /* format for ticket error messages */ span.tktError { color: #f00; font-weight: bold; } /************************************** * Timeline */ #canvas { color: #000; background-color: #fff; } div.divider { color: #ee0; font-size: 1.2rem; font-weight: bold; margin-top: 1rem; white-space: nowrap; } /* The suppressed duplicates lines in timeline, .. */ span.timelineDisabled { font-size: 0.5rem; font-style: italic; } /* the format for the timeline data table */ table.timelineTable { border: 0; } /* The row in the timeline table that contains the entry of interest */ tr.timelineSelected { border: 1px solid #eee; border-radius: 1rem; } tr.timelineSelected td.timelineTime , tr.timelineSelected td.timelineTableCell { background-color: #333; box-shadow: 2px 2px 1px #000; padding: 0.5rem; } tr.timelineSelected td.timelineTime { border-radius: 1rem 0 0 1rem; } tr.timelineSelected td.timelineTableCell { border-radius: 0 1rem 1rem 0; } /* the format for the timeline data cells */ td.timelineTableCell { padding: 0.3rem; text-align: left; vertical-align: top; } td.timelineTableCell[style] { color: #000; } /* the format for the timeline data cell of the current checkout */ tr.timelineCurrent td.timelineTableCell { border: 0; border-radius: 1em 0em; } /* the format for the timeline leaf marks */ span.timelineLeaf { font-weight: bold; } /* the format for the timeline version links */ a.timelineHistLink { } /* the format for the timeline version display(no history permission!) */ span.timelineHistDsp { font-weight: bold; } /* the format for the timeline time display */ td.timelineTime { text-align: right; vertical-align: top; white-space: nowrap; } /* the format for the grap placeholder cells in timelines */ td.timelineGraph { text-align: left; vertical-align: top; width: 20px; } /************************************** * User Edit */ /* layout definition for the capabilities box on the user edit detail page */ div.ueditCapBox { float: left; margin: 0 20px 20px 0; } /* format of the label cells in the detailed user edit page */ td.usetupEditLabel { text-align: right; vertical-align: top; white-space: nowrap; } /* color for capabilities, inherited by nobody */ span.ueditInheritNobody { color: #0f0; } /* color for capabilities, inherited by developer */ span.ueditInheritDeveloper { color: #f00; } /* color for capabilities, inherited by reader */ span.ueditInheritReader { color: black; } /* color for capabilities, inherited by anonymous */ span.ueditInheritAnonymous { color: #00f; } /* format for capabilities */ span.capability { font-weight: bold; } /* format for different user types */ span.usertype { font-weight: bold; } span.usertype:before { content:"'"; } span.usertype:after { content:"'"; } /************************************** * User List */ table.usetupLayoutTable { margin: 0.5rem; outline-style: none; padding: 0; } td.usetupColumnLayout { vertical-align: top } td.usetupColumnLayout ol th { padding: 0 0.75rem 0.5rem 0; } span.note { color: #ee0; font-weight: bold; } table.usetupUserList { margin: 0.5rem; } .usetupListUser { padding-right: 20px; text-align: right; } .usetupListCap { padding-right: 15px; text-align: center; } .usetupListCon { text-align: left; } /************************************** * Wiki */ span.wikiError { font-weight: bold; color: #f00; } /* the format for fixed/cancelled tags */ span.wikiTagCancelled { text-decoration: line-through; } /************************************** * Did not encounter these */ /* selected lines of text within a linenumbered artifact display */ div.selectedText { font-weight: bold; color: #00f; background-color: #d5d5ff; border: 1px #00f solid; } /* format for missing privileges note on user setup page */ p.missingPriv { color: #00f; } /* format for leading text in wikirules definitions */ span.wikiruleHead { font-weight: bold; } /* format for user color input on checkin edit page */ input.checkinUserColor { /* no special definitions, class defined, to enable color pickers, * f.e.: * ** add the color picker found at http:jscolor.com as java script * include * ** to the header and configure the java script file with * ** 1. use as bindClass :checkinUserColor * ** 2. change the default hash adding behaviour to ON * ** or change the class defition of element identified by * id="clrcust" * ** to a standard jscolor definition with java script in the footer. * */ } /* format for end of content area, to be used to clear page flow. */ div.endContent { clear: both; } /* format for general errors */ p.generalError { color: #f00; } /* format for tktsetup errors */ p.tktsetupError { color: #f00; font-weight: bold; } /* format for xfersetup errors */ p.xfersetupError { color: #f00; font-weight: bold; } /* format for th script errors */ p.thmainError { color: #f00; font-weight: bold; } /* format for th script trace messages */ span.thTrace { color: #f00; } /* format for report configuration errors */ p.reportError { color: #f00; font-weight: bold; } /* format for report configuration errors */ blockquote.reportError { color: #f00; font-weight: bold; } /* format for artifact lines, no longer shunned */ p.noMoreShun { color: #00f; } /* format for artifact lines beeing shunned */ p.shunned { color: #00f; } /* a broken hyperlink */ span.brokenlink { color: #f00; } /* List of files in a timeline */ ul.filelist { margin-top: 3px; line-height: 100%; } /* Moderation Pending message on timeline */ span.modpending { color: #b30; font-style: italic; } /* format for textarea labels */ span.textareaLabel { font-weight: bold; } /* format for th1 script results */ pre.th1result { white-space: pre-wrap; word-wrap: break-word; } /* format for th1 script errors */ pre.th1error { white-space: pre-wrap; word-wrap: break-word; color: #f00; } /* even table row color */ tr.row0 { /* use default */ } /* odd table row color */ tr.row1 { /* Use default */ } |
Added skins/xekri/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 0 timeline-color-graph-lines: 0 white-foreground: 0 |
Added skins/xekri/footer.txt.
> > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 | </div> <div class="footer"> <div class="page-time"> Generated in <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s </div> <div class="fossil-info"> Fossil v$release_version $manifest_version </div> </div> </body> </html> |
Added skins/xekri/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" /> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" /> </head> <body> <div class="header"> <div class="logo"> <th1> ## ## NOTE: The purpose of this procedure is to take the base URL of the ## Fossil project and return the root of the entire web site using ## the same URI scheme as the base URL (e.g. http or https). ## proc getLogoUrl { baseurl } { set idx(first) [string first // $baseurl] if {$idx(first) != -1} { ## ## NOTE: Skip second slash. ## set idx(first+1) [expr {$idx(first) + 2}] ## ## NOTE: (part 1) The [string first] command does NOT actually ## support the optional startIndex argument as specified ## in the TH1 support manual; therefore, we fake it by ## using the [string range] command and then adding the ## necessary offset to the resulting index manually ## (below). In Tcl, we could use the following instead: ## ## set idx(next) [string first / $baseurl $idx(first+1)] ## set idx(nextRange) [string range $baseurl $idx(first+1) end] set idx(next) [string first / $idx(nextRange)] if {$idx(next) != -1} { ## ## NOTE: (part 2) Add the necessary offset to the result of ## the search for the next slash (i.e. the one after ## the initial search for the two slashes). ## set idx(next) [expr {$idx(next) + $idx(first+1)}] ## ## NOTE: Back up one character from the next slash. ## set idx(next-1) [expr {$idx(next) - 1}] ## ## NOTE: Extract the URI scheme and host from the base URL. ## set scheme [string range $baseurl 0 $idx(first)] set host [string range $baseurl $idx(first+1) $idx(next-1)] ## ## NOTE: Try to stay in SSL mode if we are there now. ## if {[string compare $scheme http:/] == 0} { set scheme http:// } else { set scheme https:// } set logourl $scheme$host/ } else { set logourl $baseurl } } else { set logourl $baseurl } return $logourl } set logourl [getLogoUrl $baseurl] </th1> <a href="$logourl"> <img src="$logo_image_url" border="0" alt="$project_name"> </a> </div> <div class="title">$<title></div> <div class="status"><nobr><th1> if {[info exists login]} { puts "Logged in as $login" } else { puts "Not logged in" } </th1></nobr><small><div id="clock"></div></small></div> </div> <script> function updateClock(){ var e = document.getElementById("clock"); if(e){ var d = new Date(); function f(n) { return n < 10 ? '0' + n : n; } e.innerHTML = d.getUTCFullYear()+ '-' + f(d.getUTCMonth() + 1) + '-' + f(d.getUTCDate()) + ' ' + f(d.getUTCHours()) + ':' + f(d.getUTCMinutes()); setTimeout("updateClock();",(60-d.getUTCSeconds())*1000); } } updateClock(); </script> <div class="mainmenu"> <th1> proc menulink {url name} { upvar current_page current upvar home home if {[string range $url 0 [string length $current]] eq "/$current"} { html "<a href='$home$url' class='active'>$name</a>\n" } else { html "<a href='$home$url'>$name</a>\n" } } menulink $index_page Home if {[anycap jor]} { menulink /timeline Timeline } if {[anoncap oh]} { menulink /dir?ci=tip Files } if {[anoncap o]} { menulink /brlist Branches menulink /taglist Tags } if {[anoncap r]} { menulink /ticket Tickets } if {[anoncap j]} { menulink /wiki Wiki } if {[hascap s]} { menulink /setup Admin } elseif {[hascap a]} { menulink /setup_ulist Users } if {[info exists login]} { menulink /login Logout } else { menulink /login Login } </th1></div> |
Changes to src/add.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 | #include <assert.h> #include <dirent.h> #include "cygsup.h" /* ** This routine returns the names of files in a working checkout that ** are created by Fossil itself, and hence should not be added, deleted, | | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include <assert.h> #include <dirent.h> #include "cygsup.h" /* ** This routine returns the names of files in a working checkout that ** are created by Fossil itself, and hence should not be added, deleted, ** or merge, and should be omitted from "clean" and "extras" lists. ** ** Return the N-th name. The first name has N==0. When all names have ** been used, return 0. */ const char *fossil_reserved_name(int N, int omitRepo){ /* Possible names of the local per-checkout database file and ** its associated journals */ static const char *const azName[] = { "_FOSSIL_", "_FOSSIL_-journal", "_FOSSIL_-wal", "_FOSSIL_-shm", ".fslckout", ".fslckout-journal", ".fslckout-wal", ".fslckout-shm", /* The use of ".fos" as the name of the checkout database is ** deprecated. Use ".fslckout" instead. At some point, the following ** entries should be removed. 2012-02-04 */ ".fos", ".fos-journal", ".fos-wal", ".fos-shm", }; |
︙ | ︙ | |||
121 122 123 124 125 126 127 128 129 130 131 132 133 134 | ** ** Show all reserved filenames for the current check-out. */ void test_reserved_names(void){ int i; const char *z; int omitRepo = find_option("omitrepo",0,0)!=0; db_must_be_within_tree(); for(i=0; (z = fossil_reserved_name(i, omitRepo))!=0; i++){ fossil_print("%3d: %s\n", i, z); } fossil_print("ALL: (%s)\n", fossil_all_reserved_names(omitRepo)); } | > > > > | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | ** ** Show all reserved filenames for the current check-out. */ void test_reserved_names(void){ int i; const char *z; int omitRepo = find_option("omitrepo",0,0)!=0; /* We should be done with options.. */ verify_all_options(); db_must_be_within_tree(); for(i=0; (z = fossil_reserved_name(i, omitRepo))!=0; i++){ fossil_print("%3d: %s\n", i, z); } fossil_print("ALL: (%s)\n", fossil_all_reserved_names(omitRepo)); } |
︙ | ︙ | |||
147 148 149 150 151 152 153 154 155 156 | } if( db_exists("SELECT 1 FROM vfile" " WHERE pathname=%Q %s", zPath, filename_collation()) ){ db_multi_exec("UPDATE vfile SET deleted=0" " WHERE pathname=%Q %s", zPath, filename_collation()); }else{ char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath); db_multi_exec( "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink)" "VALUES(%d,0,0,0,%Q,%d,%d)", | > | | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | } if( db_exists("SELECT 1 FROM vfile" " WHERE pathname=%Q %s", zPath, filename_collation()) ){ db_multi_exec("UPDATE vfile SET deleted=0" " WHERE pathname=%Q %s", zPath, filename_collation()); }else{ char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath); int isExe = file_wd_isexe(zFullname); db_multi_exec( "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink)" "VALUES(%d,0,0,0,%Q,%d,%d)", vid, zPath, isExe, file_wd_islink(0)); fossil_free(zFullname); } if( db_changes() ){ fossil_print("ADDED %s\n", zPath); return 1; }else{ fossil_print("SKIP %s\n", zPath); |
︙ | ︙ | |||
175 176 177 178 179 180 181 | const char *zRepo; /* Name of the repository database file */ int nAdd = 0; /* Number of files added */ int i; /* Loop counter */ const char *zReserved; /* Name of a reserved file */ Blob repoName; /* Treename of the repository */ Stmt loop; /* SQL to loop over all files to add */ int (*xCmp)(const char*,const char*); | | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | const char *zRepo; /* Name of the repository database file */ int nAdd = 0; /* Number of files added */ int i; /* Loop counter */ const char *zReserved; /* Name of a reserved file */ Blob repoName; /* Treename of the repository */ Stmt loop; /* SQL to loop over all files to add */ int (*xCmp)(const char*,const char*); if( !file_tree_name(g.zRepositoryName, &repoName, 0) ){ blob_zero(&repoName); zRepo = ""; }else{ zRepo = blob_str(&repoName); } if( filenames_are_case_sensitive() ){ |
︙ | ︙ | |||
219 220 221 222 223 224 225 226 227 228 229 230 231 232 | ** the "--dotfiles" option to the command-line. ** ** The --ignore and --clean options are comma-separate lists of glob patterns ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore ** option does not appear on the command line then the "ignore-glob" setting ** is used. If the --clean option does not appear on the command line then ** the "clean-glob" setting is used. ** ** The --case-sensitive option determines whether or not filenames should ** be treated case sensitive or not. If the option is not given, the default ** depends on the global setting, or the operating system default, if not set. ** ** Options: ** | > > > > | | > | | | > > | > > > > < < < | > > > > > > > > > > > > > > > > > > > > | > | < | < > > | < < < < | | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | ** the "--dotfiles" option to the command-line. ** ** The --ignore and --clean options are comma-separate lists of glob patterns ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore ** option does not appear on the command line then the "ignore-glob" setting ** is used. If the --clean option does not appear on the command line then ** the "clean-glob" setting is used. ** ** If files are attempted to be added explicitly on the command line which ** match "ignore-glob", a confirmation is asked first. This can be prevented ** using the -f|--force option. ** ** The --case-sensitive option determines whether or not filenames should ** be treated case sensitive or not. If the option is not given, the default ** depends on the global setting, or the operating system default, if not set. ** ** Options: ** ** --case-sensitive <BOOL> Override the case-sensitive setting. ** --dotfiles include files beginning with a dot (".") ** -f|--force Add files without prompting ** --ignore <CSG> Ignore files matching patterns from the ** comma separated list of glob patterns. ** --clean <CSG> Also ignore files matching patterns from ** the comma separated list of glob patterns. ** ** See also: addremove, rm */ void add_cmd(void){ int i; /* Loop counter */ int vid; /* Currently checked out version */ int nRoot; /* Full path characters in g.zLocalRoot */ const char *zCleanFlag; /* The --clean option or clean-glob setting */ const char *zIgnoreFlag; /* The --ignore option or ignore-glob setting */ Glob *pIgnore, *pClean; /* Ignore everything matching the glob patterns */ unsigned scanFlags = 0; /* Flags passed to vfile_scan() */ int forceFlag; zCleanFlag = find_option("clean",0,1); zIgnoreFlag = find_option("ignore",0,1); forceFlag = find_option("force","f",0)!=0; if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; /* We should be done with options.. */ verify_all_options(); db_must_be_within_tree(); if( zCleanFlag==0 ){ zCleanFlag = db_get("clean-glob", 0); } if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL; vid = db_lget_int("checkout",0); db_begin_transaction(); db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", filename_collation()); pClean = glob_create(zCleanFlag); pIgnore = glob_create(zIgnoreFlag); nRoot = strlen(g.zLocalRoot); /* Load the names of all files that are to be added into sfile temp table */ for(i=2; i<g.argc; i++){ char *zName; int isDir; Blob fullName; /* file_tree_name() throws a fatal error if g.argv[i] is outside of the ** checkout. */ file_tree_name(g.argv[i], &fullName, 1); blob_reset(&fullName); file_canonical_name(g.argv[i], &fullName, 0); zName = blob_str(&fullName); isDir = file_wd_isdir(zName); if( isDir==1 ){ vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore); }else if( isDir==0 ){ fossil_warning("not found: %s", zName); }else if( file_access(zName, R_OK) ){ fossil_fatal("cannot open %s", zName); }else{ char *zTreeName = &zName[nRoot]; if( !forceFlag && glob_match(pIgnore, zTreeName) ){ Blob ans; char cReply; char *prompt = mprintf("file \"%s\" matches \"ignore-glob\". " "Add it (a=all/y/N)? ", zTreeName); prompt_user(prompt, &ans); cReply = blob_str(&ans)[0]; blob_reset(&ans); if( cReply=='a' || cReply=='A' ){ forceFlag = 1; }else if( cReply!='y' && cReply!='Y' ){ blob_reset(&fullName); continue; } } db_multi_exec( "INSERT OR IGNORE INTO sfile(x) VALUES(%Q)", zTreeName ); } blob_reset(&fullName); } glob_free(pIgnore); glob_free(pClean); add_files_in_sfile(vid); db_end_transaction(0); } /* ** COMMAND: rm ** COMMAND: delete ** COMMAND: forget* ** ** Usage: %fossil rm|delete|forget FILE1 ?FILE2 ...? ** ** Remove one or more files or directories from the repository. ** ** This command does NOT remove the files from disk. It just marks the ** files as no longer being part of the project. In other words, future ** changes to the named files will not be versioned. ** ** Options: ** --case-sensitive <BOOL> Override the case-sensitive setting. ** ** See also: addremove, add */ void delete_cmd(void){ int i; Stmt loop; /* We should be done with options.. */ verify_all_options(); db_must_be_within_tree(); db_begin_transaction(); db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", filename_collation()); for(i=2; i<g.argc; i++){ Blob treeName; char *zTreeName; file_tree_name(g.argv[i], &treeName, 1); zTreeName = blob_str(&treeName); db_multi_exec( "INSERT OR IGNORE INTO sfile" " SELECT pathname FROM vfile" " WHERE (pathname=%Q %s" " OR (pathname>'%q/' %s AND pathname<'%q0' %s))" " AND NOT deleted", zTreeName, filename_collation(), zTreeName, filename_collation(), zTreeName, filename_collation() ); blob_reset(&treeName); } db_prepare(&loop, "SELECT x FROM sfile"); while( db_step(&loop)==SQLITE_ROW ){ fossil_print("DELETED %s\n", db_column_text(&loop, 0)); } db_finalize(&loop); db_multi_exec( "UPDATE vfile SET deleted=1 WHERE pathname IN sfile;" |
︙ | ︙ | |||
412 413 414 415 416 417 418 | #else caseSensitive = 1; /* Unix */ #endif caseSensitive = db_get_boolean("case-sensitive",caseSensitive); } if( !caseSensitive && g.localOpen ){ db_multi_exec( | | | > | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | #else caseSensitive = 1; /* Unix */ #endif caseSensitive = db_get_boolean("case-sensitive",caseSensitive); } if( !caseSensitive && g.localOpen ){ db_multi_exec( "CREATE INDEX IF NOT EXISTS %s.vfile_nocase" " ON vfile(pathname COLLATE nocase)", db_name("localdb") ); } } return caseSensitive; } /* |
︙ | ︙ | |||
440 441 442 443 444 445 446 | ** ** Usage: %fossil addremove ?OPTIONS? ** ** Do all necessary "add" and "rm" commands to synchronize the repository ** with the content of the working checkout: ** ** * All files in the checkout but not in the repository (that is, | | | > | | | | | | | | > > > > < < < | | 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 | ** ** Usage: %fossil addremove ?OPTIONS? ** ** Do all necessary "add" and "rm" commands to synchronize the repository ** with the content of the working checkout: ** ** * All files in the checkout but not in the repository (that is, ** all files displayed using the "extras" command) are added as ** if by the "add" command. ** ** * All files in the repository but missing from the checkout (that is, ** all files that show as MISSING with the "status" command) are ** removed as if by the "rm" command. ** ** The command does not "commit". You must run the "commit" separately ** as a separate step. ** ** Files and directories whose names begin with "." are ignored unless ** the --dotfiles option is used. ** ** The --ignore option overrides the "ignore-glob" setting, as do the ** --case-sensitive option with the "case-sensitive" setting and the ** --clean option with the "clean-glob" setting. See the documentation ** on the "settings" command for further information. ** ** The -n|--dry-run option shows what would happen without actually doing ** anything. ** ** This command can be used to track third party software. ** ** Options: ** --case-sensitive <BOOL> Override the case-sensitive setting. ** --dotfiles Include files beginning with a dot (".") ** --ignore <CSG> Ignore files matching patterns from the ** comma separated list of glob patterns. ** --clean <CSG> Also ignore files matching patterns from ** the comma separated list of glob patterns. ** -n|--dry-run If given, display instead of run actions. ** ** See also: add, rm */ void addremove_cmd(void){ Blob path; const char *zCleanFlag = find_option("clean",0,1); const char *zIgnoreFlag = find_option("ignore",0,1); unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; int dryRunFlag = find_option("dry-run","n",0)!=0; int n; Stmt q; int vid; int nAdd = 0; int nDelete = 0; Glob *pIgnore, *pClean; if( !dryRunFlag ){ dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ } /* We should be done with options.. */ verify_all_options(); db_must_be_within_tree(); if( zCleanFlag==0 ){ zCleanFlag = db_get("clean-glob", 0); } if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL; vid = db_lget_int("checkout",0); db_begin_transaction(); /* step 1: ** Populate the temp table "sfile" with the names of all unmanaged ** files currently in the check-out, except for files that match the ** --ignore or ignore-glob patterns and dot-files. Then add all of ** the files in the sfile temp table to the set of managed files. */ db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", filename_collation()); |
︙ | ︙ | |||
529 530 531 532 533 534 535 | db_prepare(&q, "SELECT pathname, %Q || pathname, deleted FROM vfile" " WHERE NOT deleted" " ORDER BY 1", g.zLocalRoot ); while( db_step(&q)==SQLITE_ROW ){ | | | | 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 | db_prepare(&q, "SELECT pathname, %Q || pathname, deleted FROM vfile" " WHERE NOT deleted" " ORDER BY 1", g.zLocalRoot ); while( db_step(&q)==SQLITE_ROW ){ const char *zFile; const char *zPath; zFile = db_column_text(&q, 0); zPath = db_column_text(&q, 1); if( !file_wd_isfile_or_link(zPath) ){ if( !dryRunFlag ){ db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zFile); } |
︙ | ︙ | |||
555 556 557 558 559 560 561 | /* ** Rename a single file. ** ** The original name of the file is zOrig. The new filename is zNew. */ | | > > > > | | | 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 | /* ** Rename a single file. ** ** The original name of the file is zOrig. The new filename is zNew. */ static void mv_one_file( int vid, const char *zOrig, const char *zNew ){ int x = db_int(-1, "SELECT deleted FROM vfile WHERE pathname=%Q %s", zNew, filename_collation()); if( x>=0 ){ if( x==0 ){ fossil_fatal("cannot rename '%s' to '%s' since another file named '%s'" " is currently under management", zOrig, zNew, zNew); }else{ fossil_fatal("cannot rename '%s' to '%s' since the delete of '%s' has " "not yet been committed", zOrig, zNew, zNew); } } fossil_print("RENAME %s %s\n", zOrig, zNew); db_multi_exec( |
︙ | ︙ | |||
586 587 588 589 590 591 592 | ** or: %fossil mv|rename OLDNAME... DIR ** ** Move or rename one or more files or directories within the repository tree. ** You can either rename a file or directory or move it to another subdirectory. ** ** This command does NOT rename or move the files on disk. This command merely ** records the fact that filenames have changed so that appropriate notations | | | < > > > > | 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 | ** or: %fossil mv|rename OLDNAME... DIR ** ** Move or rename one or more files or directories within the repository tree. ** You can either rename a file or directory or move it to another subdirectory. ** ** This command does NOT rename or move the files on disk. This command merely ** records the fact that filenames have changed so that appropriate notations ** can be made at the next commit/check-in. ** ** Options: ** --case-sensitive <BOOL> Override the case-sensitive setting. ** ** See also: changes, status */ void mv_cmd(void){ int i; int vid; char *zDest; Blob dest; Stmt q; db_must_be_within_tree(); /* We should be done with options.. */ verify_all_options(); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("no checkout rename files in"); } if( g.argc<4 ){ usage("OLDNAME NEWNAME"); } |
︙ | ︙ | |||
674 675 676 677 678 679 680 | const char *zFrom = db_column_text(&q, 0); const char *zTo = db_column_text(&q, 1); mv_one_file(vid, zFrom, zTo); } db_finalize(&q); db_end_transaction(0); } | > > > > > > > > | 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 | const char *zFrom = db_column_text(&q, 0); const char *zTo = db_column_text(&q, 1); mv_one_file(vid, zFrom, zTo); } db_finalize(&q); db_end_transaction(0); } /* ** Function for stash_apply to be able to restore a file and indicate ** newly ADDED state. */ int stash_add_files_in_sfile(int vid){ return add_files_in_sfile(vid); } |
Changes to src/allrepo.c.
︙ | ︙ | |||
56 57 58 59 60 61 62 | if( find_option(zArg, zShort, 0)!=0 ){ blob_appendf(pExtra, " --%s", zArg); } } static void collect_argument_value(Blob *pExtra, const char *zArg){ const char *zValue = find_option(zArg, 0, 1); if( zValue ){ | > | > > > > > > > > > | | > > | > | | > > | | > > | | > > > > | > | > | > > > | > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > | > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > | > | > | | | > | | > > | > | | < > | < | < | < > > > | | > > > > > > > > > > > | > > | | < < < < | < < < < < | | < | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | if( find_option(zArg, zShort, 0)!=0 ){ blob_appendf(pExtra, " --%s", zArg); } } static void collect_argument_value(Blob *pExtra, const char *zArg){ const char *zValue = find_option(zArg, 0, 1); if( zValue ){ if( zValue[0] ){ blob_appendf(pExtra, " --%s %s", zArg, zValue); }else{ blob_appendf(pExtra, " --%s \"\"", zArg); } } } static void collect_argv(Blob *pExtra, int iStart){ int i; for(i=iStart; i<g.argc; i++){ blob_appendf(pExtra, " %s", g.argv[i]); } } /* ** COMMAND: all ** ** Usage: %fossil all SUBCOMMAND ... ** ** The ~/.fossil file records the location of all repositories for a ** user. This command performs certain operations on all repositories ** that can be useful before or after a period of disconnected operation. ** ** On Win32 systems, the file is named "_fossil" and is located in ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. ** ** Available operations are: ** ** changes Shows all local checkouts that have uncommitted changes. ** This operation has no additional options. ** ** clean Delete all "extra" files in all local checkouts. Extreme ** caution should be exercised with this command because its ** effects cannot be undone. Use of the --dry-run option to ** carefully review the local checkouts to be operated upon ** and the --whatif option to carefully review the files to ** be deleted beforehand is highly recommended. The command ** line options supported by the clean command itself, if any ** are present, are passed along verbatim. ** ** dbstat Run the "dbstat" command on all repositories. ** ** extras Shows "extra" files from all local checkouts. The command ** line options supported by the extra command itself, if any ** are present, are passed along verbatim. ** ** fts-config Run the "fts-config" command on all repositories. ** ** info Run the "info" command on all repositories. ** ** pull Run a "pull" operation on all repositories. Only the ** --verbose option is supported. ** ** push Run a "push" on all repositories. Only the --verbose ** option is supported. ** ** rebuild Rebuild on all repositories. The command line options ** supported by the rebuild command itself, if any are ** present, are passed along verbatim. The --force and ** --randomize options are not supported. ** ** sync Run a "sync" on all repositories. Only the --verbose ** option is supported. ** ** setting Run the "setting", "set", or "unset" commands on all ** set repositories. These command are particularly useful in ** unset conjunction with the "max-loadavg" setting which cannot ** otherwise be set globally. ** ** In addition, the following maintenance operations are supported: ** ** add Add all the repositories named to the set of repositories ** tracked by Fossil. Normally Fossil is able to keep up with ** this list by itself, but sometime it can benefit from this ** hint if you rename repositories. ** ** ignore Arguments are repositories that should be ignored by ** subsequent clean, extras, list, pull, push, rebuild, and ** sync operations. The -c|--ckout option causes the listed ** local checkouts to be ignored instead. ** ** list | ls Display the location of all repositories. The -c|--ckout ** option causes all local checkouts to be listed instead. ** ** Repositories are automatically added to the set of known repositories ** when one of the following commands are run against the repository: ** clone, info, pull, push, or sync. Even previously ignored repositories ** are added back to the list of repositories by these commands. ** ** Options: ** --showfile Show the repository or checkout being operated upon. ** --dontstop Continue with other repositories even after an error. ** --dry-run If given, display instead of run actions. */ void all_cmd(void){ int n; Stmt q; const char *zCmd; char *zSyscmd; char *zFossil; char *zQFilename; Blob extra; int useCheckouts = 0; int quiet = 0; int dryRunFlag = 0; int showFile = find_option("showfile",0,0)!=0; int stopOnError = find_option("dontstop",0,0)==0; int rc; int rowCount, i = 0; char **azFilename = 0; char **azTag = 0; int nToDel = 0; int showLabel = 0; dryRunFlag = find_option("dry-run","n",0)!=0; if( !dryRunFlag ){ dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ } if( g.argc<3 ){ usage("SUBCOMMAND ..."); } n = strlen(g.argv[2]); db_open_config(1); blob_zero(&extra); zCmd = g.argv[2]; if( !login_is_nobody() ) blob_appendf(&extra, " -U %s", g.zLogin); if( strncmp(zCmd, "list", n)==0 || strncmp(zCmd,"ls",n)==0 ){ zCmd = "list"; useCheckouts = find_option("ckout","c",0)!=0; }else if( strncmp(zCmd, "clean", n)==0 ){ zCmd = "clean --chdir"; collect_argument(&extra, "allckouts",0); collect_argument_value(&extra, "case-sensitive"); collect_argument_value(&extra, "clean"); collect_argument(&extra, "dirsonly",0); collect_argument(&extra, "dotfiles",0); collect_argument(&extra, "emptydirs",0); collect_argument(&extra, "force","f"); collect_argument_value(&extra, "ignore"); collect_argument_value(&extra, "keep"); collect_argument(&extra, "temp",0); collect_argument(&extra, "verbose","v"); collect_argument(&extra, "whatif",0); useCheckouts = 1; }else if( strncmp(zCmd, "dbstat", n)==0 ){ zCmd = "dbstat --omit-version-info -R"; showLabel = 1; quiet = 1; collect_argument(&extra, "brief", "b"); collect_argument(&extra, "db-check", 0); }else if( strncmp(zCmd, "extras", n)==0 ){ if( showFile ){ zCmd = "extras --chdir"; }else{ zCmd = "extras --header --chdir"; } collect_argument(&extra, "abs-paths",0); collect_argument_value(&extra, "case-sensitive"); collect_argument(&extra, "dotfiles",0); collect_argument_value(&extra, "ignore"); collect_argument(&extra, "rel-paths",0); useCheckouts = 1; stopOnError = 0; quiet = 1; }else if( strncmp(zCmd, "push", n)==0 ){ zCmd = "push -autourl -R"; collect_argument(&extra, "verbose","v"); }else if( strncmp(zCmd, "pull", n)==0 ){ zCmd = "pull -autourl -R"; collect_argument(&extra, "verbose","v"); }else if( strncmp(zCmd, "rebuild", n)==0 ){ zCmd = "rebuild"; collect_argument(&extra, "cluster",0); collect_argument(&extra, "compress",0); collect_argument(&extra, "noverify",0); collect_argument_value(&extra, "pagesize"); collect_argument(&extra, "vacuum",0); collect_argument(&extra, "deanalyze",0); collect_argument(&extra, "analyze",0); collect_argument(&extra, "wal",0); collect_argument(&extra, "stats",0); collect_argument(&extra, "index",0); collect_argument(&extra, "noindex",0); collect_argument(&extra, "ifneeded", 0); }else if( strncmp(zCmd, "setting", n)==0 ){ zCmd = "setting -R"; collect_argv(&extra, 3); }else if( strncmp(zCmd, "unset", n)==0 ){ zCmd = "unset -R"; collect_argv(&extra, 3); }else if( strncmp(zCmd, "fts-config", n)==0 ){ zCmd = "fts-config -R"; collect_argv(&extra, 3); }else if( strncmp(zCmd, "sync", n)==0 ){ zCmd = "sync -autourl -R"; collect_argument(&extra, "verbose","v"); }else if( strncmp(zCmd, "test-integrity", n)==0 ){ collect_argument(&extra, "parse", 0); zCmd = "test-integrity"; }else if( strncmp(zCmd, "test-orphans", n)==0 ){ zCmd = "test-orphans -R"; }else if( strncmp(zCmd, "test-missing", n)==0 ){ zCmd = "test-missing -q -R"; collect_argument(&extra, "notshunned",0); }else if( strncmp(zCmd, "changes", n)==0 ){ zCmd = "changes --quiet --header --chdir"; useCheckouts = 1; stopOnError = 0; quiet = 1; }else if( strncmp(zCmd, "ignore", n)==0 ){ int j; Blob fn = BLOB_INITIALIZER; Blob sql = BLOB_INITIALIZER; useCheckouts = find_option("ckout","c",0)!=0; verify_all_options(); db_begin_transaction(); for(j=3; j<g.argc; j++, blob_reset(&sql), blob_reset(&fn)){ file_canonical_name(g.argv[j], &fn, 0); blob_append_sql(&sql, "DELETE FROM global_config WHERE name GLOB '%s:%q'", useCheckouts?"ckout":"repo", blob_str(&fn) ); if( dryRunFlag ){ fossil_print("%s\n", blob_sql_text(&sql)); }else{ db_multi_exec("%s", blob_sql_text(&sql)); } } db_end_transaction(0); return; }else if( strncmp(zCmd, "add", n)==0 ){ int j; Blob fn = BLOB_INITIALIZER; Blob sql = BLOB_INITIALIZER; verify_all_options(); db_begin_transaction(); for(j=3; j<g.argc; j++, blob_reset(&fn), blob_reset(&sql)){ sqlite3 *db; int rc; const char *z; file_canonical_name(g.argv[j], &fn, 0); z = blob_str(&fn); if( !file_isfile(z) ) continue; rc = sqlite3_open(z, &db); if( rc!=SQLITE_OK ){ sqlite3_close(db); continue; } rc = sqlite3_exec(db, "SELECT rcvid FROM blob, delta LIMIT 1", 0, 0, 0); sqlite3_close(db); if( rc!=SQLITE_OK ) continue; blob_append_sql(&sql, "INSERT INTO global_config(name,value)VALUES('repo:%q',1)", z ); if( dryRunFlag ){ fossil_print("%s\n", blob_sql_text(&sql)); }else{ db_multi_exec("%s", blob_sql_text(&sql)); } } db_end_transaction(0); return; }else if( strncmp(zCmd, "info", n)==0 ){ zCmd = "info"; showLabel = 1; quiet = 1; }else{ fossil_fatal("\"all\" subcommand should be one of: " "add changes clean dbstat extras fts-config ignore " "info list ls pull push rebuild setting sync unset"); } verify_all_options(); zFossil = quoteFilename(g.nameOfExe); db_multi_exec("CREATE TEMP TABLE repolist(name,tag);"); if( useCheckouts ){ db_multi_exec( "INSERT INTO repolist " "SELECT DISTINCT substr(name, 7), name COLLATE nocase" " FROM global_config" " WHERE substr(name, 1, 6)=='ckout:'" " ORDER BY 1" ); }else{ db_multi_exec( "INSERT INTO repolist " "SELECT DISTINCT substr(name, 6), name COLLATE nocase" " FROM global_config" " WHERE substr(name, 1, 5)=='repo:'" " ORDER BY 1" ); } db_prepare(&q, "SELECT name, tag FROM repolist ORDER BY 1"); rowCount = db_all_column_text(&q, 0, &azFilename, 1, &azTag); db_finalize(&q); db_multi_exec("CREATE TEMP TABLE todel(x TEXT)"); while( i<rowCount ){ const char *zFilename = azFilename[i]; const char *zTag = azTag[i]; if( file_access(zFilename, F_OK) || !file_is_canonical(zFilename) || (useCheckouts && file_isdir(zFilename)!=1) ){ db_multi_exec("INSERT INTO todel VALUES(%Q)", zTag); nToDel++; i++; continue; } if( zCmd[0]=='l' ){ fossil_print("%s\n", zFilename); i++; continue; }else if( showFile ){ fossil_print("%s: %s\n", useCheckouts ? "checkout" : "repository", zFilename); } zQFilename = quoteFilename(zFilename); zSyscmd = mprintf("%s %s %s%s", zFossil, zCmd, zQFilename, blob_str(&extra)); if( showLabel ){ int len = (int)strlen(zFilename); int nStar = 80 - (len + 15); if( nStar<2 ) nStar = 1; fossil_print("%.13c %s %.*c\n", '*', zFilename, nStar, '*'); } if( !quiet || dryRunFlag ){ fossil_print("%s\n", zSyscmd); fflush(stdout); } rc = dryRunFlag ? 0 : fossil_system(zSyscmd); free(zSyscmd); free(zQFilename); if( stopOnError && rc ){ break; } i++; } db_all_column_free(rowCount, &azFilename); db_all_column_free(rowCount, &azTag); assert( !azFilename ); assert( !azTag ); /* If any repositories whose names appear in the ~/.fossil file could not ** be found, remove those names from the ~/.fossil file. */ if( nToDel>0 ){ const char *zSql = "DELETE FROM global_config WHERE name IN toDel"; if( dryRunFlag ){ fossil_print("%s\n", zSql); }else{ db_multi_exec("%s", zSql /*safe-for-%s*/ ); } } } |
Changes to src/attach.c.
︙ | ︙ | |||
27 28 29 30 31 32 33 | ** tkt=TICKETUUID ** page=WIKIPAGE ** ** List attachments. ** Either one of tkt= or page= are supplied or neither. If neither ** are given, all attachments are listed. If one is given, only ** attachments for the designated ticket or wiki page are shown. | | | | < > | | | | | | > > > | | | | | | | | | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | ** tkt=TICKETUUID ** page=WIKIPAGE ** ** List attachments. ** Either one of tkt= or page= are supplied or neither. If neither ** are given, all attachments are listed. If one is given, only ** attachments for the designated ticket or wiki page are shown. ** TICKETUUID must be complete */ void attachlist_page(void){ const char *zPage = P("page"); const char *zTkt = P("tkt"); Blob sql; Stmt q; if( zPage && zTkt ) zTkt = 0; login_check_credentials(); blob_zero(&sql); blob_append_sql(&sql, "SELECT datetime(mtime%s), src, target, filename," " comment, user," " (SELECT uuid FROM blob WHERE rid=attachid), attachid" " FROM attachment", timeline_utc() ); if( zPage ){ if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; } style_header("Attachments To %h", zPage); blob_append_sql(&sql, " WHERE target=%Q", zPage); }else if( zTkt ){ if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; } style_header("Attachments To Ticket %S", zTkt); blob_append_sql(&sql, " WHERE target GLOB '%q*'", zTkt); }else{ if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ){ login_needed(g.anon.RdTkt || g.anon.RdWiki); return; } style_header("All Attachments"); } blob_append_sql(&sql, " ORDER BY mtime DESC"); db_prepare(&q, "%s", blob_sql_text(&sql)); @ <ol> while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zSrc = db_column_text(&q, 1); const char *zTarget = db_column_text(&q, 2); const char *zFilename = db_column_text(&q, 3); const char *zComment = db_column_text(&q, 4); const char *zUser = db_column_text(&q, 5); const char *zUuid = db_column_text(&q, 6); int attachid = db_column_int(&q, 7); const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; int i; char *zUrlTail; for(i=0; zFilename[i]; i++){ if( zFilename[i]=='/' && zFilename[i+1]!=0 ){ zFilename = &zFilename[i+1]; i = -1; } } if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){ zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename); }else{ zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename); } @ <li><p> @ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a> if( moderation_pending(attachid) ){ @ <span class="modpending">*** Awaiting Moderator Approval ***</span> } @ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; if( zComment && zComment[0] ){ @ %!W(zComment)<br /> } if( zPage==0 && zTkt==0 ){ if( zSrc==0 || zSrc[0]==0 ){ zSrc = "Deleted from"; }else { zSrc = "Added to"; } if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){ @ %s(zSrc) ticket <a href="%R/tktview?name=%s(zTarget)"> @ %S(zTarget)</a> }else{ @ %s(zSrc) wiki page <a href="%R/wiki?name=%t(zTarget)"> @ %h(zTarget)</a> } }else{ if( zSrc==0 || zSrc[0]==0 ){ @ Deleted }else { @ Added |
︙ | ︙ | |||
148 149 150 151 152 153 154 | int attachid = atoi(PD("attachid","0")); char *zUUID; if( zPage && zTkt ) zTkt = 0; if( zFile==0 ) fossil_redirect_home(); login_check_credentials(); if( zPage ){ | | | | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | int attachid = atoi(PD("attachid","0")); char *zUUID; if( zPage && zTkt ) zTkt = 0; if( zFile==0 ) fossil_redirect_home(); login_check_credentials(); if( zPage ){ if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; } zTarget = zPage; }else if( zTkt ){ if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; } zTarget = zTkt; }else{ fossil_redirect_home(); } if( attachid>0 ){ zUUID = db_text(0, "SELECT coalesce(src,'x') FROM attachment" |
︙ | ︙ | |||
212 213 214 215 216 217 218 | rid, attachRid ); }else{ rid = content_put(pAttach); db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); } | | | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | rid, attachRid ); }else{ rid = content_put(pAttach); db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); } manifest_crosslink(rid, pAttach, MC_NONE); } /* ** WEBPAGE: attachadd ** ** tkt=TICKETUUID |
︙ | ︙ | |||
241 242 243 244 245 246 247 | int goodCaptcha = 1; if( P("cancel") ) cgi_redirect(zFrom); if( zPage && zTkt ) fossil_redirect_home(); if( zPage==0 && zTkt==0 ) fossil_redirect_home(); login_check_credentials(); if( zPage ){ | | > > > | | | > > > | | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | int goodCaptcha = 1; if( P("cancel") ) cgi_redirect(zFrom); if( zPage && zTkt ) fossil_redirect_home(); if( zPage==0 && zTkt==0 ) fossil_redirect_home(); login_check_credentials(); if( zPage ){ if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){ login_needed(g.anon.ApndWiki && g.anon.Attach); return; } if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zPage) ){ fossil_redirect_home(); } zTarget = zPage; zTargetType = mprintf("Wiki Page <a href=\"%R/wiki?name=%h\">%h</a>", zPage, zPage); }else{ if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){ login_needed(g.anon.ApndTkt && g.anon.Attach); return; } if( !db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", zTkt) ){ zTkt = db_text(0, "SELECT substr(tagname,5) FROM tag" " WHERE tagname GLOB 'tkt-%q*'", zTkt); if( zTkt==0 ) fossil_redirect_home(); } zTarget = zTkt; zTargetType = mprintf("Ticket <a href=\"%R/tktview/%s\">%S</a>", zTkt, zTkt); } if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop); if( P("cancel") ){ cgi_redirect(zFrom); } if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){ Blob content; |
︙ | ︙ | |||
286 287 288 289 290 291 292 | manifest_destroy(pManifest); blob_init(&content, aContent, szContent); if( pManifest ){ blob_compress(&content, &content); addCompress = 1; } needModerator = | | | | | 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | manifest_destroy(pManifest); blob_init(&content, aContent, szContent); if( pManifest ){ blob_compress(&content, &content); addCompress = 1; } needModerator = (zTkt!=0 && ticket_need_moderation(0)) || (zPage!=0 && wiki_need_moderation(0)); rid = content_put_ex(&content, 0, 0, 0, needModerator); zUUID = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); blob_zero(&manifest); for(i=n=0; zName[i]; i++){ if( zName[i]=='/' || zName[i]=='\\' ) n = i; } zName += n; if( zName[0]==0 ) zName = "unknown"; blob_appendf(&manifest, "A %F%s %F %s\n", zName, addCompress ? ".gz" : "", zTarget, zUUID); zComment = PD("comment", ""); while( fossil_isspace(zComment[0]) ) zComment++; n = strlen(zComment); while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; } if( n>0 ){ blob_appendf(&manifest, "C %#F\n", n, zComment); } zDate = date_in_standard_format("now"); blob_appendf(&manifest, "D %s\n", zDate); blob_appendf(&manifest, "U %F\n", login_name()); md5sum_blob(&manifest, &cksum); blob_appendf(&manifest, "Z %b\n", &cksum); attach_put(&manifest, rid, needModerator); assert( blob_is_reset(&manifest) ); db_end_transaction(0); cgi_redirect(zFrom); } |
︙ | ︙ | |||
363 364 365 366 367 368 369 370 371 | const char *zWikiName = 0; /* Wiki page name when attached to Wiki */ const char *zTktUuid = 0; /* Ticket ID when attached to a ticket */ int modPending; /* True if awaiting moderation */ const char *zModAction; /* Moderation action or NULL */ int isModerator; /* TRUE if user is the moderator */ const char *zMime; /* MIME Type */ Blob attach; /* Content of the attachment */ login_check_credentials(); | > > | > > > | | > > | | | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | const char *zWikiName = 0; /* Wiki page name when attached to Wiki */ const char *zTktUuid = 0; /* Ticket ID when attached to a ticket */ int modPending; /* True if awaiting moderation */ const char *zModAction; /* Moderation action or NULL */ int isModerator; /* TRUE if user is the moderator */ const char *zMime; /* MIME Type */ Blob attach; /* Content of the attachment */ int fShowContent = 0; const char *zLn = P("ln"); login_check_credentials(); if( !g.perm.RdTkt && !g.perm.RdWiki ){ login_needed(g.anon.RdTkt || g.anon.RdWiki); return; } rid = name_to_rid_www("name"); if( rid==0 ){ fossil_redirect_home(); } zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); #if 0 /* Shunning here needs to get both the attachment control artifact and ** the object that is attached. */ if( g.perm.Admin ){ if( db_exists("SELECT 1 FROM shun WHERE uuid='%q'", zUuid) ){ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1", g.zTop, zUuid); }else{ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); } } #endif pAttach = manifest_get(rid, CFTYPE_ATTACHMENT, 0); if( pAttach==0 ) fossil_redirect_home(); zTarget = pAttach->zAttachTarget; zSrc = pAttach->zAttachSrc; ridSrc = db_int(0,"SELECT rid FROM blob WHERE uuid='%q'", zSrc); zName = pAttach->zAttachName; zDesc = pAttach->zComment; zMime = mimetype_from_name(zName); fShowContent = zMime ? strncmp(zMime,"text/", 5)==0 : 0; if( validate16(zTarget, strlen(zTarget)) && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid='%q'", zTarget) ){ zTktUuid = zTarget; if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } if( g.perm.WrTkt ){ style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid); } }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'",zTarget) ){ zWikiName = zTarget; if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } if( g.perm.WrWiki ){ style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid); } } zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate); if( P("confirm") |
︙ | ︙ | |||
425 426 427 428 429 430 431 | if( zFile[i]=='/' || zFile[i]=='\\' ) n = i; } zFile += n; if( zFile[0]==0 ) zFile = "unknown"; blob_appendf(&manifest, "A %F %F\n", zFile, zTarget); zDate = date_in_standard_format("now"); blob_appendf(&manifest, "D %s\n", zDate); | | | | | | | > > > > > | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | if( zFile[i]=='/' || zFile[i]=='\\' ) n = i; } zFile += n; if( zFile[0]==0 ) zFile = "unknown"; blob_appendf(&manifest, "A %F %F\n", zFile, zTarget); zDate = date_in_standard_format("now"); blob_appendf(&manifest, "D %s\n", zDate); blob_appendf(&manifest, "U %F\n", login_name()); md5sum_blob(&manifest, &cksum); blob_appendf(&manifest, "Z %b\n", &cksum); rid = content_put(&manifest); manifest_crosslink(rid, &manifest, MC_NONE); db_end_transaction(0); @ <p>The attachment below has been deleted.</p> } if( P("del") && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki)) ){ form_begin(0, "%R/ainfo/%!S", zUuid); @ <p>Confirm you want to delete the attachment shown below. @ <input type="submit" name="confirm" value="Confirm"> @ </form> } isModerator = g.perm.Admin || (zTktUuid && g.perm.ModTkt) || (zWikiName && g.perm.ModWiki); if( isModerator && (zModAction = P("modaction"))!=0 ){ if( strcmp(zModAction,"delete")==0 ){ moderation_disapprove(rid); if( zTktUuid ){ cgi_redirectf("%R/tktview/%!S", zTktUuid); }else{ cgi_redirectf("%R/wiki?name=%t", zWikiName); } return; } if( strcmp(zModAction,"approve")==0 ){ moderation_approve(rid); } } style_header("Attachment Details"); style_submenu_element("Raw", "Raw", "%R/artifact/%s", zUuid); if(fShowContent){ style_submenu_element("Line Numbers", "Line Numbers", "%R/ainfo/%s%s",zUuid, ((zLn&&*zLn) ? "" : "?ln=0")); } @ <div class="section">Overview</div> @ <p><table class="label-value"> @ <tr><th>Artifact ID:</th> @ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a> if( g.perm.Setup ){ @ (%d(rid)) } modPending = moderation_pending(rid); if( modPending ){ @ <span class="modpending">*** Awaiting Moderator Approval ***</span> } |
︙ | ︙ | |||
492 493 494 495 496 497 498 | hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>"); @ <tr><th>Artifact Attached:</th> @ <td>%z(href("%R/artifact/%s",zSrc))%s(zSrc)</a> if( g.perm.Setup ){ @ (%d(ridSrc)) } @ <tr><th>Filename:</th><td>%h(zName)</td></tr> | < | | < | | | | | | | | | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 | hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>"); @ <tr><th>Artifact Attached:</th> @ <td>%z(href("%R/artifact/%s",zSrc))%s(zSrc)</a> if( g.perm.Setup ){ @ (%d(ridSrc)) } @ <tr><th>Filename:</th><td>%h(zName)</td></tr> if( g.perm.Setup ){ @ <tr><th>MIME-Type:</th><td>%h(zMime)</td></tr> } @ <tr><th valign="top">Description:</th><td valign="top">%h(zDesc)</td></tr> @ </table> if( isModerator && modPending ){ @ <div class="section">Moderation</div> @ <blockquote> form_begin(0, "%R/ainfo/%s", zUuid); @ <label><input type="radio" name="modaction" value="delete"> @ Delete this change</label><br /> @ <label><input type="radio" name="modaction" value="approve"> @ Approve this change</label><br /> @ <input type="submit" value="Submit"> @ </form> @ </blockquote> } @ <div class="section">Content Appended</div> @ <blockquote> blob_zero(&attach); if( fShowContent ){ const char *z; content_get(ridSrc, &attach); blob_to_utf8_no_bom(&attach, 0); z = blob_str(&attach); if( zLn ){ output_text_with_line_numbers(z, zLn); }else{ @ <pre> @ %h(z) @ </pre> } }else if( strncmp(zMime, "image/", 6)==0 ){ @ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img> style_submenu_element("Image", "Image", "%R/raw/%s?m=%s", zSrc, zMime); }else{ int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc); @ <i>(file is %d(sz) bytes of binary data)</i> } @ </blockquote> manifest_destroy(pAttach); blob_reset(&attach); style_footer(); } /* ** Output HTML to show a list of attachments. */ void attachment_list( const char *zTarget, /* Object that things are attached to */ const char *zHeader /* Header to display with attachments */ ){ int cnt = 0; Stmt q; db_prepare(&q, "SELECT datetime(mtime%s), filename, user," " (SELECT uuid FROM blob WHERE rid=attachid), src" " FROM attachment" " WHERE isLatest AND src!='' AND target=%Q" " ORDER BY mtime DESC", timeline_utc(), zTarget ); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zFile = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); const char *zUuid = db_column_text(&q, 3); const char *zSrc = db_column_text(&q, 4); const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; if( cnt==0 ){ @ %s(zHeader) } cnt++; @ <li> @ %z(href("%R/artifact/%!S",zSrc))%h(zFile)</a> @ added by %h(zDispUser) on hyperlink_to_date(zDate, "."); @ [%z(href("%R/ainfo/%!S",zUuid))details</a>] @ </li> } if( cnt ){ @ </ul> } db_finalize(&q); } |
Changes to src/bag.c.
︙ | ︙ | |||
73 74 75 76 77 78 79 | #define bag_hash(i) (i*101) /* ** Change the size of the hash table on a bag so that ** it contains N slots ** ** Completely reconstruct the hash table from scratch. Deleted | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | #define bag_hash(i) (i*101) /* ** Change the size of the hash table on a bag so that ** it contains N slots ** ** Completely reconstruct the hash table from scratch. Deleted ** entries (indicated by a -1) are removed. When finished, it ** should be the case that p->cnt==p->used. */ static void bag_resize(Bag *p, int newSize){ int i; Bag old; int nDel = 0; /* Number of deleted entries */ int nLive = 0; /* Number of live entries */ |
︙ | ︙ |
Changes to src/bisect.c.
︙ | ︙ | |||
204 205 206 207 208 209 210 | db_finalize(&q); db_prepare(&q, "SELECT bilog.seq, bilog.stat," " substr(blob.uuid,1,16), datetime(event.mtime)" " FROM bilog, blob, event" " WHERE blob.rid=bilog.rid AND event.objid=bilog.rid" " AND event.type='ci'" | | | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | db_finalize(&q); db_prepare(&q, "SELECT bilog.seq, bilog.stat," " substr(blob.uuid,1,16), datetime(event.mtime)" " FROM bilog, blob, event" " WHERE blob.rid=bilog.rid AND event.objid=bilog.rid" " AND event.type='ci'" " ORDER BY %s bilog.rowid ASC", (sortByCkinTime ? "event.mtime DESC, " : "") ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%3d %-7s %s %s\n", db_column_int(&q, 0), db_column_text(&q, 1), db_column_text(&q, 3), db_column_text(&q, 2)); |
︙ | ︙ | |||
368 369 370 371 372 373 374 | }else{ g.argv[1] = "update"; g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid); g.argc = 3; g.fNoSync = 1; update_cmd(); } | | | | | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | }else{ g.argv[1] = "update"; g.argv[2] = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pMid->rid); g.argc = 3; g.fNoSync = 1; update_cmd(); } if( strncmp(zDisplay,"chart",m)==0 ){ bisect_chart(1); }else if( strncmp(zDisplay, "log", m)==0 ){ bisect_chart(0); }else if( strncmp(zDisplay, "status", m)==0 ){ bisect_list(1); } }else if( strncmp(zCmd, "log", n)==0 ){ bisect_chart(0); }else if( strncmp(zCmd, "chart", n)==0 ){ bisect_chart(1); }else if( strncmp(zCmd, "options", n)==0 ){ if( g.argc==3 ){ unsigned int i; for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){ char *z = mprintf("bisect-%s", aBisectOption[i].zName); fossil_print(" %-15s %-6s ", aBisectOption[i].zName, db_lget(z, (char*)aBisectOption[i].zDefault)); fossil_free(z); comment_print(aBisectOption[i].zDesc, 0, 27, -1, g.comFmtFlags); } }else if( g.argc==4 || g.argc==5 ){ unsigned int i; n = strlen(g.argv[3]); for(i=0; i<sizeof(aBisectOption)/sizeof(aBisectOption[0]); i++){ if( strncmp(g.argv[3], aBisectOption[i].zName, n)==0 ){ char *z = mprintf("bisect-%s", aBisectOption[i].zName); if( g.argc==5 ){ db_lset(z, g.argv[4]); } fossil_print("%s\n", db_lget(z, (char*)aBisectOption[i].zDefault)); fossil_free(z); break; |
︙ | ︙ |
Changes to src/blob.c.
︙ | ︙ | |||
15 16 17 18 19 20 21 | ** ******************************************************************************* ** ** A Blob is a variable-length containers for arbitrary string ** or binary data. */ #include "config.h" | > > > > | > > > > > > > > > > > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | ** ******************************************************************************* ** ** A Blob is a variable-length containers for arbitrary string ** or binary data. */ #include "config.h" #if defined(FOSSIL_ENABLE_MINIZ) # define MINIZ_HEADER_FILE_ONLY # include "miniz.c" #else # include <zlib.h> #endif #include "blob.h" #if defined(_WIN32) #include <fcntl.h> #include <io.h> #endif #if INTERFACE /* ** A Blob can hold a string or a binary object of arbitrary size. The ** size changes as necessary. */ struct Blob { unsigned int nUsed; /* Number of bytes used in aData[] */ unsigned int nAlloc; /* Number of bytes allocated for aData[] */ unsigned int iCursor; /* Next character of input to parse */ unsigned int blobFlags; /* One or more BLOBFLAG_* bits */ char *aData; /* Where the information is stored */ void (*xRealloc)(Blob*, unsigned int); /* Function to reallocate the buffer */ }; /* ** Allowed values for Blob.blobFlags */ #define BLOBFLAG_NotSQL 0x0001 /* Non-SQL text */ /* ** The current size of a Blob */ #define blob_size(X) ((X)->nUsed) /* ** The buffer holding the blob data |
︙ | ︙ | |||
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | void blobReallocMalloc(Blob *pBlob, unsigned int newSize){ if( newSize==0 ){ free(pBlob->aData); pBlob->aData = 0; pBlob->nAlloc = 0; pBlob->nUsed = 0; pBlob->iCursor = 0; }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){ char *pNew = fossil_realloc(pBlob->aData, newSize); pBlob->aData = pNew; pBlob->nAlloc = newSize; if( pBlob->nUsed>pBlob->nAlloc ){ pBlob->nUsed = pBlob->nAlloc; } } } /* ** An initializer for Blobs */ #if INTERFACE | > | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | void blobReallocMalloc(Blob *pBlob, unsigned int newSize){ if( newSize==0 ){ free(pBlob->aData); pBlob->aData = 0; pBlob->nAlloc = 0; pBlob->nUsed = 0; pBlob->iCursor = 0; pBlob->blobFlags = 0; }else if( newSize>pBlob->nAlloc || newSize<pBlob->nAlloc-4000 ){ char *pNew = fossil_realloc(pBlob->aData, newSize); pBlob->aData = pNew; pBlob->nAlloc = newSize; if( pBlob->nUsed>pBlob->nAlloc ){ pBlob->nUsed = pBlob->nAlloc; } } } /* ** An initializer for Blobs */ #if INTERFACE #define BLOB_INITIALIZER {0,0,0,0,0,blobReallocMalloc} #endif const Blob empty_blob = BLOB_INITIALIZER; /* ** A reallocation function for when the initial string is in unmanaged ** space. Copy the string to memory obtained from malloc(). */ |
︙ | ︙ | |||
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | if( zData==0 ){ *pBlob = empty_blob; }else{ if( size<=0 ) size = strlen(zData); pBlob->nUsed = pBlob->nAlloc = size; pBlob->aData = (char*)zData; pBlob->iCursor = 0; pBlob->xRealloc = blobReallocStatic; } } /* ** Initialize a blob to a nul-terminated string. ** Any prior data in the blob is discarded. */ void blob_set(Blob *pBlob, const char *zStr){ blob_init(pBlob, zStr, -1); } /* ** Initialize a blob to an empty string. */ void blob_zero(Blob *pBlob){ static const char zEmpty[] = ""; assert_blob_is_reset(pBlob); pBlob->nUsed = 0; pBlob->nAlloc = 1; pBlob->aData = (char*)zEmpty; pBlob->iCursor = 0; pBlob->xRealloc = blobReallocStatic; } /* ** Append text or data to the end of a blob. */ void blob_append(Blob *pBlob, const char *aData, int nData){ | > > > > > > > > > > > | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | if( zData==0 ){ *pBlob = empty_blob; }else{ if( size<=0 ) size = strlen(zData); pBlob->nUsed = pBlob->nAlloc = size; pBlob->aData = (char*)zData; pBlob->iCursor = 0; pBlob->blobFlags = 0; pBlob->xRealloc = blobReallocStatic; } } /* ** Initialize a blob to a nul-terminated string. ** Any prior data in the blob is discarded. */ void blob_set(Blob *pBlob, const char *zStr){ blob_init(pBlob, zStr, -1); } /* ** Initialize a blob to a nul-terminated string obtained from fossil_malloc(). ** The blob will take responsibility for freeing the string. */ void blob_set_dynamic(Blob *pBlob, char *zStr){ blob_init(pBlob, zStr, -1); pBlob->xRealloc = blobReallocMalloc; } /* ** Initialize a blob to an empty string. */ void blob_zero(Blob *pBlob){ static const char zEmpty[] = ""; assert_blob_is_reset(pBlob); pBlob->nUsed = 0; pBlob->nAlloc = 1; pBlob->aData = (char*)zEmpty; pBlob->iCursor = 0; pBlob->blobFlags = 0; pBlob->xRealloc = blobReallocStatic; } /* ** Append text or data to the end of a blob. */ void blob_append(Blob *pBlob, const char *aData, int nData){ |
︙ | ︙ | |||
276 277 278 279 280 281 282 283 284 285 286 287 288 289 | p->nUsed = 0; } if( p->aData[p->nUsed]!=0 ){ blob_materialize(p); } return p->aData; } /* ** Return a pointer to a null-terminated string for a blob. ** ** WARNING: If the blob is ephemeral, it might cause a '\000' ** character to be inserted into the middle of the parent blob. ** Example: Suppose p is a token extracted from some larger | > > > > > > > > > > > > > > | 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | p->nUsed = 0; } if( p->aData[p->nUsed]!=0 ){ blob_materialize(p); } return p->aData; } /* ** Return a pointer to a null-terminated string for a blob that has ** been created using blob_append_sql() and not blob_appendf(). If ** text was ever added using blob_appendf() then throw an error. */ char *blob_sql_text(Blob *p){ blob_is_init(p); if( (p->blobFlags & BLOBFLAG_NotSQL) ){ fossil_fatal("Internal error: Use of blob_appendf() to construct SQL text"); } return blob_str(p); } /* ** Return a pointer to a null-terminated string for a blob. ** ** WARNING: If the blob is ephemeral, it might cause a '\000' ** character to be inserted into the middle of the parent blob. ** Example: Suppose p is a token extracted from some larger |
︙ | ︙ | |||
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 | int i; for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){} return i; } /* ** Do printf-style string rendering and append the results to a blob. */ void blob_appendf(Blob *pBlob, const char *zFormat, ...){ if( pBlob ){ va_list ap; va_start(ap, zFormat); vxprintf(pBlob, zFormat, ap); va_end(ap); } } void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){ | > > > > > > > > > > > > | 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 | int i; for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){} return i; } /* ** Do printf-style string rendering and append the results to a blob. ** ** The blob_appendf() version sets the BLOBFLAG_NotSQL bit in Blob.blobFlags ** whereas blob_append_sql() does not. */ void blob_appendf(Blob *pBlob, const char *zFormat, ...){ if( pBlob ){ va_list ap; va_start(ap, zFormat); vxprintf(pBlob, zFormat, ap); va_end(ap); pBlob->blobFlags |= BLOBFLAG_NotSQL; } } void blob_append_sql(Blob *pBlob, const char *zFormat, ...){ if( pBlob ){ va_list ap; va_start(ap, zFormat); vxprintf(pBlob, zFormat, ap); va_end(ap); } } void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){ |
︙ | ︙ | |||
698 699 700 701 702 703 704 | /* ** Initialize a blob to be the content of a file. If the filename ** is blank or "-" then read from standard input. ** ** Any prior content of the blob is discarded, not freed. ** | | | 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 | /* ** Initialize a blob to be the content of a file. If the filename ** is blank or "-" then read from standard input. ** ** Any prior content of the blob is discarded, not freed. ** ** Return the number of bytes read. Calls fossil_fatal() on error (i.e. ** it exit()s and does not return). */ int blob_read_from_file(Blob *pBlob, const char *zFilename){ int size, got; FILE *in; if( zFilename==0 || zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ |
︙ | ︙ | |||
772 773 774 775 776 777 778 779 780 | if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ nWrote = blob_size(pBlob); #if defined(_WIN32) if( fossil_utf8_to_console(blob_buffer(pBlob), nWrote, 0) >= 0 ){ return nWrote; } #endif fwrite(blob_buffer(pBlob), 1, nWrote, stdout); | > > < < < < < < < < < < < < < < < | < < < < < < > > < < < | < < < | < < | | > < | 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 | if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ nWrote = blob_size(pBlob); #if defined(_WIN32) if( fossil_utf8_to_console(blob_buffer(pBlob), nWrote, 0) >= 0 ){ return nWrote; } fflush(stdout); _setmode(_fileno(stdout), _O_BINARY); #endif fwrite(blob_buffer(pBlob), 1, nWrote, stdout); #if defined(_WIN32) fflush(stdout); _setmode(_fileno(stdout), _O_TEXT); #endif }else{ file_mkfolder(zFilename, 1); out = fossil_fopen(zFilename, "wb"); if( out==0 ){ fossil_fatal_recursive("unable to open file \"%s\" for writing", zFilename); return 0; } blob_is_init(pBlob); nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out); fclose(out); if( nWrote!=blob_size(pBlob) ){ fossil_fatal_recursive("short write: %d of %d bytes to %s", nWrote, blob_size(pBlob), zFilename); } |
︙ | ︙ | |||
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 | for(i=j=0; z[i]; i++){ if( z[i]!='\r' ) z[j++] = z[i]; else if( z[i+1]!='\n' ) z[j++] = '\n'; } z[j] = 0; p->nUsed = j; } /* ** Shell-escape the given string. Append the result to a blob. */ void shell_escape(Blob *pBlob, const char *zIn){ int n = blob_size(pBlob); int k = strlen(zIn); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 | for(i=j=0; z[i]; i++){ if( z[i]!='\r' ) z[j++] = z[i]; else if( z[i+1]!='\n' ) z[j++] = '\n'; } z[j] = 0; p->nUsed = j; } /* ** Convert blob from cp1252 to UTF-8. As cp1252 is a superset ** of iso8859-1, this is useful on UNIX as well. ** ** This table contains the character translations for 0x80..0xA0. */ static const unsigned short cp1252[32] = { 0x20ac, 0x81, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x8D, 0x017D, 0x8F, 0x90, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x2DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x9D, 0x017E, 0x0178 }; void blob_cp1252_to_utf8(Blob *p){ unsigned char *z = (unsigned char *)p->aData; int j = p->nUsed; int i, n; for(i=n=0; i<j; i++){ if( z[i]>=0x80 ){ if( (z[i]<0xa0) && (cp1252[z[i]&0x1f]>=0x800) ){ n++; } n++; } } j += n; if( j>=p->nAlloc ){ blob_resize(p, j); z = (unsigned char *)p->aData; } p->nUsed = j; z[j] = 0; while( j>i ){ if( z[--i]>=0x80 ){ if( z[i]<0xa0 ){ unsigned short sym = cp1252[z[i]&0x1f]; if( sym>=0x800 ){ z[--j] = 0x80 | (sym&0x3f); z[--j] = 0x80 | ((sym>>6)&0x3f); z[--j] = 0xe0 | (sym>>12); }else{ z[--j] = 0x80 | (sym&0x3f); z[--j] = 0xc0 | (sym>>6); } }else{ z[--j] = 0x80 | (z[i]&0x3f); z[--j] = 0xC0 | (z[i]>>6); } }else{ z[--j] = z[i]; } } } /* ** Shell-escape the given string. Append the result to a blob. */ void shell_escape(Blob *pBlob, const char *zIn){ int n = blob_size(pBlob); int k = strlen(zIn); |
︙ | ︙ | |||
1093 1094 1095 1096 1097 1098 1099 | ** is either no BOM at all or an (le/be) UTF-16 BOM, a conversion to UTF-8 is ** done. If useMbcs is false and there is no BOM, the input string is assumed ** to be UTF-8 already, so no conversion is done. */ void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){ char *zUtf8; int bomSize = 0; | < < < < | < < > | < > > | 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 | ** is either no BOM at all or an (le/be) UTF-16 BOM, a conversion to UTF-8 is ** done. If useMbcs is false and there is no BOM, the input string is assumed ** to be UTF-8 already, so no conversion is done. */ void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){ char *zUtf8; int bomSize = 0; int bomReverse = 0; if( starts_with_utf8_bom(pBlob, &bomSize) ){ struct Blob temp; zUtf8 = blob_str(pBlob) + bomSize; blob_zero(&temp); blob_append(&temp, zUtf8, -1); blob_swap(pBlob, &temp); blob_reset(&temp); }else if( starts_with_utf16_bom(pBlob, &bomSize, &bomReverse) ){ zUtf8 = blob_buffer(pBlob); if( bomReverse ){ /* Found BOM, but with reversed bytes */ unsigned int i = blob_size(pBlob); while( i>0 ){ /* swap bytes of unicode representation */ char zTemp = zUtf8[--i]; zUtf8[i] = zUtf8[i-1]; zUtf8[--i] = zTemp; } } /* Make sure the blob contains two terminating 0-bytes */ blob_append(pBlob, "", 1); zUtf8 = blob_str(pBlob) + bomSize; zUtf8 = fossil_unicode_to_utf8(zUtf8); blob_set_dynamic(pBlob, zUtf8); }else if( useMbcs && invalid_utf8(pBlob) ){ #if defined(_WIN32) || defined(__CYGWIN__) zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob)); blob_reset(pBlob); blob_append(pBlob, zUtf8, -1); fossil_mbcs_free(zUtf8); #else blob_cp1252_to_utf8(pBlob); #endif /* _WIN32 */ } } |
Changes to src/branch.c.
︙ | ︙ | |||
49 50 51 52 53 54 55 | zDateOvrd = find_option("date-override",0,1); zUserOvrd = find_option("user-override",0,1); verify_all_options(); if( g.argc<5 ){ usage("new BRANCH-NAME BASIS ?OPTIONS?"); } db_find_and_open_repository(0, 0); | | > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | zDateOvrd = find_option("date-override",0,1); zUserOvrd = find_option("user-override",0,1); verify_all_options(); if( g.argc<5 ){ usage("new BRANCH-NAME BASIS ?OPTIONS?"); } db_find_and_open_repository(0, 0); noSign = db_get_boolean("omitsign", 0)|noSign; if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } /* fossil branch new name */ zBranch = g.argv[3]; if( zBranch==0 || zBranch[0]==0 ){ fossil_fatal("branch name cannot be empty"); } if( db_exists( |
︙ | ︙ | |||
131 132 133 134 135 136 137 | rootid); while( db_step(&q)==SQLITE_ROW ){ const char *zTag = db_column_text(&q, 0); blob_appendf(&branch, "T -%F *\n", zTag); } db_finalize(&q); | | < | | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | rootid); while( db_step(&q)==SQLITE_ROW ){ const char *zTag = db_column_text(&q, 0); blob_appendf(&branch, "T -%F *\n", zTag); } db_finalize(&q); blob_appendf(&branch, "U %F\n", zUserOvrd ? zUserOvrd : login_name()); md5sum_blob(&branch, &mcksum); blob_appendf(&branch, "Z %b\n", &mcksum); if( !noSign && clearsign(&branch, &branch) ){ Blob ans; char cReply; prompt_user("unable to sign manifest. continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y'){ db_end_transaction(1); fossil_exit(1); } } brid = content_put_ex(&branch, 0, 0, 0, isPrivate); if( brid==0 ){ fossil_fatal("trouble committing manifest: %s", g.zErrMsg); } db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ fossil_fatal("%s\n", g.zErrMsg); } assert( blob_is_reset(&branch) ); content_deltify(rootid, brid, 0); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid); fossil_print("New branch: %s\n", zUuid); if( g.argc==3 ){ fossil_print( |
︙ | ︙ | |||
174 175 176 177 178 179 180 | } /* Commit */ db_end_transaction(0); /* Do an autosync push, if requested */ | | > > > > > > > > > > > > > | | > | | | | | | | | | | | > > | | | | | | | | | > > | | | | | | | | > > | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | } /* Commit */ db_end_transaction(0); /* Do an autosync push, if requested */ if( !isPrivate ) autosync_loop(SYNC_PUSH, db_get_int("autosync-tries", 1)); } #if INTERFACE /* ** Allows bits in the mBplqFlags parameter to branch_prepare_list_query(). */ #define BRL_CLOSED_ONLY 0x001 /* Show only closed branches */ #define BRL_OPEN_ONLY 0x002 /* Show only open branches */ #define BRL_BOTH 0x003 /* Show both open and closed branches */ #define BRL_OPEN_CLOSED_MASK 0x003 #define BRL_MTIME 0x004 /* Include lastest check-in time */ #dfeine BRL_ORDERBY_MTIME 0x008 /* Sort by MTIME. (otherwise sort by name)*/ #endif /* INTERFACE */ /* ** Prepare a query that will list branches. ** ** If (which<0) then the query pulls only closed branches. If ** (which>0) then the query pulls all (closed and opened) ** branches. Else the query pulls currently-opened branches. */ void branch_prepare_list_query(Stmt *pQuery, int brFlags){ switch( brFlags & BRL_OPEN_CLOSED_MASK ){ case BRL_CLOSED_ONLY: { db_prepare(pQuery, "SELECT value FROM tagxref" " WHERE tagid=%d AND value NOT NULL " "EXCEPT " "SELECT value FROM tagxref" " WHERE tagid=%d" " AND rid IN leaf" " AND NOT %z" " ORDER BY value COLLATE nocase /*sort*/", TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") ); break; } case BRL_BOTH: { db_prepare(pQuery, "SELECT DISTINCT value FROM tagxref" " WHERE tagid=%d AND value NOT NULL" " AND rid IN leaf" " ORDER BY value COLLATE nocase /*sort*/", TAG_BRANCH ); break; } case BRL_OPEN_ONLY: { db_prepare(pQuery, "SELECT DISTINCT value FROM tagxref" " WHERE tagid=%d AND value NOT NULL" " AND rid IN leaf" " AND NOT %z" " ORDER BY value COLLATE nocase /*sort*/", TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") ); break; } } } /* ** COMMAND: branch ** |
︙ | ︙ | |||
250 251 252 253 254 255 256 | ** Options: ** -R|--repository FILE Run commands on repository FILE */ void branch_cmd(void){ int n; const char *zCmd = "list"; db_find_and_open_repository(0, 0); | < < < > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > > | | | > | > | | | | | | < < < < < < | 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | ** Options: ** -R|--repository FILE Run commands on repository FILE */ void branch_cmd(void){ int n; const char *zCmd = "list"; db_find_and_open_repository(0, 0); if( g.argc>=3 ) zCmd = g.argv[2]; n = strlen(zCmd); if( strncmp(zCmd,"new",n)==0 ){ branch_new(); }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", n)==0) ){ Stmt q; int vid; char *zCurrent = 0; int brFlags = BRL_OPEN_ONLY; if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH; if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY; if( g.localOpen ){ vid = db_lget_int("checkout", 0); zCurrent = db_text(0, "SELECT value FROM tagxref" " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH); } branch_prepare_list_query(&q, brFlags); while( db_step(&q)==SQLITE_ROW ){ const char *zBr = db_column_text(&q, 0); int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0; fossil_print("%s%s\n", (isCur ? "* " : " "), zBr); } db_finalize(&q); }else{ fossil_fatal("branch subcommand should be one of: " "new list ls"); } } static const char brlistQuery[] = @ SELECT @ tagxref.value, @ max(event.mtime), @ EXISTS(SELECT 1 FROM tagxref AS tx @ WHERE tx.rid=tagxref.rid @ AND tx.tagid=(SELECT tagid FROM tag WHERE tagname='closed') @ AND tx.tagtype>0), @ (SELECT tagxref.value @ FROM plink CROSS JOIN tagxref @ WHERE plink.pid=event.objid @ AND tagxref.rid=plink.cid @ AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='branch') @ AND tagtype>0), @ count(*), @ (SELECT uuid FROM blob WHERE rid=tagxref.rid) @ FROM tagxref, tag, event @ WHERE tagxref.tagid=tag.tagid @ AND tagxref.tagtype>0 @ AND tag.tagname='branch' @ AND event.objid=tagxref.rid @ GROUP BY 1 @ ORDER BY 2 DESC; ; /* ** This is the new-style branch-list page that shows the branch names ** together with their ages (time of last check-in) and whether or not ** they are closed or merged to another branch. ** ** Control jumps to this routine from brlist_page() (the /brlist handler) ** if there are no query parameters. */ static void new_brlist_page(void){ Stmt q; double rNow; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("Branches"); style_adunit_config(ADUNIT_RIGHT_OK); login_anonymous_available(); db_prepare(&q, brlistQuery/*works-like:""*/); rNow = db_double(0.0, "SELECT julianday('now')"); @ <div class="brlist"><table id="branchlisttable"> @ <thead><tr> @ <th>Branch Name</th> @ <th>Age</th> @ <th>Check-ins</th> @ <th>Status</th> @ <th>Resolution</th> @ </tr></thead><tbody> while( db_step(&q)==SQLITE_ROW ){ const char *zBranch = db_column_text(&q, 0); double rMtime = db_column_double(&q, 1); int isClosed = db_column_int(&q, 2); const char *zMergeTo = db_column_text(&q, 3); int nCkin = db_column_int(&q, 4); const char *zLastCkin = db_column_text(&q, 5); char *zAge = human_readable_age(rNow - rMtime); sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0; @ <tr> @ <td>%z(href("%R/timeline?n=100&r=%T",zBranch))%h(zBranch)</a></td> @ <td data-sortkey="%016llx(-iMtime)">%s(zAge)</td> @ <td>%d(nCkin)</td> fossil_free(zAge); @ <td>%s(isClosed?"closed":"")</td> if( zMergeTo ){ @ <td>merged into @ %z(href("%R/timeline?f=%!S",zLastCkin))%h(zMergeTo)</a></td> }else{ @ <td></td> } @ </tr> } @ </tbody></table></div> db_finalize(&q); output_table_sorting_javascript("branchlisttable","tkNtt",2); style_footer(); } /* ** WEBPAGE: brlist ** Show a list of branches ** Query parameters: ** ** all Show all branches ** closed Show only closed branches ** open Show only open branches (default behavior) ** colortest Show all branches with automatic color */ void brlist_page(void){ Stmt q; int cnt; int showClosed = P("closed")!=0; int showAll = P("all")!=0; int showOpen = P("open")!=0; int colorTest = P("colortest")!=0; int brFlags = BRL_OPEN_ONLY; if( showClosed==0 && showAll==0 && showOpen==0 && colorTest==0 ){ new_brlist_page(); return; } login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( colorTest ){ showClosed = 0; showAll = 1; } if( showAll ) brFlags = BRL_BOTH; if( showClosed ) brFlags = BRL_CLOSED_ONLY; style_header("%s", showClosed ? "Closed Branches" : showAll ? "All Branches" : "Open Branches"); style_submenu_element("Timeline", "Timeline", "brtimeline"); if( showClosed ){ style_submenu_element("All", "All", "brlist?all"); style_submenu_element("Open","Open","brlist?open"); }else if( showAll ){ style_submenu_element("Closed", "Closed", "brlist?closed"); style_submenu_element("Open","Open","brlist"); }else{ style_submenu_element("All", "All", "brlist?all"); style_submenu_element("Closed","Closed","brlist?closed"); } if( !colorTest ){ style_submenu_element("Color-Test", "Color-Test", "brlist?colortest"); }else{ style_submenu_element("All", "All", "brlist?all"); } login_anonymous_available(); #if 0 style_sidebox_begin("Nomenclature:", "33%"); @ <ol> @ <li> An <div class="sideboxDescribed">%z(href("brlist")) @ open branch</a></div> is a branch that has one or more @ <div class="sideboxDescribed">%z(href("leaves"))open leaves.</a></div> @ The presence of open leaves presumably means @ that the branch is still being extended with new check-ins.</li> @ <li> A <div class="sideboxDescribed">%z(href("brlist?closed")) @ closed branch</a></div> is a branch with only @ <div class="sideboxDescribed">%z(href("leaves?closed")) @ closed leaves</a></div>. @ Closed branches are fixed and do not change (unless they are first @ reopened).</li> @ </ol> style_sidebox_end(); #endif branch_prepare_list_query(&q, brFlags); cnt = 0; while( db_step(&q)==SQLITE_ROW ){ const char *zBr = db_column_text(&q, 0); if( cnt==0 ){ if( colorTest ){ @ <h2>Default background colors for all branches:</h2> }else if( showClosed ){ @ <h2>Closed Branches:</h2> }else if( showAll ){ @ <h2>All Branches:</h2> }else{ @ <h2>Open Branches:</h2> } @ <ul> cnt++; } if( colorTest ){ const char *zColor = hash_color(zBr); @ <li><span style="background-color: %s(zColor)"> @ %h(zBr) → %s(zColor)</span></li> }else{ @ <li>%z(href("%R/timeline?r=%T&n=200",zBr))%h(zBr)</a></li> } } if( cnt ){ @ </ul> } db_finalize(&q); style_footer(); } /* ** This routine is called while for each check-in that is rendered by ** the timeline of a "brlist" page. Add some additional hyperlinks ** to the end of the line. |
︙ | ︙ | |||
392 393 394 395 396 397 398 | " AND tagxref.tagid=tag.tagid" " AND tagxref.tagtype>0" " AND tag.tagname GLOB 'sym-*'", rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zTagName = db_column_text(&q, 0); | | | | < < < < < < | 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 | " AND tagxref.tagid=tag.tagid" " AND tagxref.tagtype>0" " AND tag.tagname GLOB 'sym-*'", rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zTagName = db_column_text(&q, 0); @ %z(href("%R/timeline?r=%T&n=200",zTagName))[timeline]</a> } db_finalize(&q); } /* ** WEBPAGE: brtimeline ** ** Show a timeline of all branches */ void brtimeline_page(void){ Stmt q; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("Branches"); style_submenu_element("List", "List", "brlist"); login_anonymous_available(); @ <h2>The initial check-in for each branch:</h2> db_prepare(&q, "%s AND blob.rid IN (SELECT rid FROM tagxref" " WHERE tagtype>0 AND tagid=%d AND srcid!=0)" " ORDER BY event.mtime DESC", timeline_query_for_www(), TAG_BRANCH ); www_print_timeline(&q, 0, 0, 0, 0, brtimeline_extra); db_finalize(&q); style_footer(); } |
Changes to src/browse.c.
︙ | ︙ | |||
69 70 71 72 73 74 75 | ** to the "dir" page for the directory. ** ** There is no hyperlink on the file element of the path. ** ** The computed string is appended to the pOut blob. pOut should ** have already been initialized. */ | | > > > > > > | | | | > > > | > > > | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | ** to the "dir" page for the directory. ** ** There is no hyperlink on the file element of the path. ** ** The computed string is appended to the pOut blob. pOut should ** have already been initialized. */ void hyperlinked_path( const char *zPath, /* Path to render */ Blob *pOut, /* Write into this blob */ const char *zCI, /* check-in name, or NULL */ const char *zURI, /* "dir" or "tree" */ const char *zREx /* Extra query parameters */ ){ int i, j; char *zSep = ""; for(i=0; zPath[i]; i=j){ for(j=i; zPath[j] && zPath[j]!='/'; j++){} if( zPath[j] && g.perm.Hyperlink ){ if( zCI ){ char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI); blob_appendf(pOut, "%s%z%#h</a>", zSep, zLink, j-i, &zPath[i]); }else{ char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx); blob_appendf(pOut, "%s%z%#h</a>", zSep, zLink, j-i, &zPath[i]); } }else{ blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]); } zSep = "/"; while( zPath[j]=='/' ){ j++; } } } /* ** WEBPAGE: dir ** ** Query parameters: ** ** name=PATH Directory to display. Optional. Top-level if missing ** ci=LABEL Show only files in this check-in. Optional. */ void page_dir(void){ char *zD = fossil_strdup(P("name")); int nD = zD ? strlen(zD)+1 : 0; int mxLen; int nCol, nRow; int cnt, i; char *zPrefix; Stmt q; const char *zCI = P("ci"); int rid = 0; char *zUuid = 0; Blob dirname; Manifest *pM = 0; const char *zSubdirLink; int linkTrunk = 1; int linkTip = 1; HQuery sURI; if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; } login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } style_header("File List"); style_adunit_config(ADUNIT_RIGHT_OK); sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, pathelementFunc, 0, 0); url_initialize(&sURI, "dir"); cgi_query_parameters_to_url(&sURI); /* If the name= parameter is an empty string, make it a NULL pointer */ if( zD && strlen(zD)==0 ){ zD = 0; } /* If a specific check-in is requested, fetch and parse it. If the ** specific check-in does not exist, clear zCI. zCI==0 will cause all ** files from all check-ins to be displayed. |
︙ | ︙ | |||
148 149 150 151 152 153 154 | } } /* Compute the title of the page */ blob_zero(&dirname); if( zD ){ blob_append(&dirname, "in directory ", -1); | | < | < < < < < > > | | > | | | > | < < < < | | | < < < < | > > > > | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | } } /* Compute the title of the page */ blob_zero(&dirname); if( zD ){ blob_append(&dirname, "in directory ", -1); hyperlinked_path(zD, &dirname, zCI, "dir", ""); zPrefix = mprintf("%s/", zD); style_submenu_element("Top-Level", "Top-Level", "%s", url_render(&sURI, "name", 0, 0, 0)); }else{ blob_append(&dirname, "in the top-level directory", -1); zPrefix = ""; } if( linkTrunk ){ style_submenu_element("Trunk", "Trunk", "%s", url_render(&sURI, "ci", "trunk", 0, 0)); } if( linkTip ){ style_submenu_element("Tip", "Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); } if( zCI ){ @ <h2>Files of check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] @ %s(blob_str(&dirname))</h2> zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix); if( nD==0 ){ style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%!S", zUuid); } }else{ @ <h2>The union of all files from all check-ins @ %s(blob_str(&dirname))</h2> zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); } style_submenu_element("All", "All", "%s", url_render(&sURI, "ci", 0, 0, 0)); style_submenu_element("Tree-View", "Tree-View", "%s", url_render(&sURI, "type", "tree", 0, 0)); /* Compute the temporary table "localfiles" containing the names ** of all files and subdirectories in the zD[] directory. ** ** Subdirectory names begin with "/". This causes them to sort ** first and it also gives us an easy way to distinguish files ** from directories in the loop that follows. |
︙ | ︙ | |||
273 274 275 276 277 278 279 | if( zFN[0]=='/' ){ zFN++; @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> }else{ const char *zLink; if( zCI ){ const char *zUuid = db_column_text(&q, 1); | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > < < < < | < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < | < > | | | | | | | < < | > | > | > > > > > > | > > > > > > > > > > > | > > | | > > > | < < < < | > > > | > > > | < < | > > > > > > > | > | > | < | | > > > > | | < | | > | | > > > > > | > > | > < < | < < < < > > | | | | < | > > > > > > | > > > > > > | < > < > < < > | | > > | > > | > > | > < | | > | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 | if( zFN[0]=='/' ){ zFN++; @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li> }else{ const char *zLink; if( zCI ){ const char *zUuid = db_column_text(&q, 1); zLink = href("%R/artifact/%!S",zUuid); }else{ zLink = href("%R/finfo?name=%T%T",zPrefix,zFN); } @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li> } } db_finalize(&q); manifest_destroy(pM); @ </ul></td></tr></table> style_footer(); } /* ** Objects used by the "tree" webpage. */ typedef struct FileTreeNode FileTreeNode; typedef struct FileTree FileTree; /* ** A single line of the file hierarchy */ struct FileTreeNode { FileTreeNode *pNext; /* Next entry in an ordered list of them all */ FileTreeNode *pParent; /* Directory containing this entry */ FileTreeNode *pSibling; /* Next element in the same subdirectory */ FileTreeNode *pChild; /* List of child nodes */ FileTreeNode *pLastChild; /* Last child on the pChild list */ char *zName; /* Name of this entry. The "tail" */ char *zFullName; /* Full pathname of this entry */ char *zUuid; /* SHA1 hash of this file. May be NULL. */ double mtime; /* Modification time for this entry */ unsigned nFullName; /* Length of zFullName */ unsigned iLevel; /* Levels of parent directories */ }; /* ** A complete file hierarchy */ struct FileTree { FileTreeNode *pFirst; /* First line of the list */ FileTreeNode *pLast; /* Last line of the list */ FileTreeNode *pLastTop; /* Last top-level node */ }; /* ** Add one or more new FileTreeNodes to the FileTree object so that the ** leaf object zPathname is at the end of the node list. ** ** The caller invokes this routine once for each leaf node (each file ** as opposed to each directory). This routine fills in any missing ** intermediate nodes automatically. ** ** When constructing a list of FileTreeNodes, all entries that have ** a common directory prefix must be added consecutively in order for ** the tree to be constructed properly. */ static void tree_add_node( FileTree *pTree, /* Tree into which nodes are added */ const char *zPath, /* The full pathname of file to add */ const char *zUuid, /* UUID of the file. Might be NULL. */ double mtime /* Modification time for this entry */ ){ int i; FileTreeNode *pParent; /* Parent (directory) of the next node to insert */ /* Make pParent point to the most recent ancestor of zPath, or ** NULL if there are no prior entires that are a container for zPath. */ pParent = pTree->pLast; while( pParent!=0 && ( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0 || zPath[pParent->nFullName]!='/' ) ){ pParent = pParent->pParent; } i = pParent ? pParent->nFullName+1 : 0; while( zPath[i] ){ FileTreeNode *pNew; int iStart = i; int nByte; while( zPath[i] && zPath[i]!='/' ){ i++; } nByte = sizeof(*pNew) + i + 1; if( zUuid!=0 && zPath[i]==0 ) nByte += UUID_SIZE+1; pNew = fossil_malloc( nByte ); memset(pNew, 0, sizeof(*pNew)); pNew->zFullName = (char*)&pNew[1]; memcpy(pNew->zFullName, zPath, i); pNew->zFullName[i] = 0; pNew->nFullName = i; if( zUuid!=0 && zPath[i]==0 ){ pNew->zUuid = pNew->zFullName + i + 1; memcpy(pNew->zUuid, zUuid, UUID_SIZE+1); } pNew->zName = pNew->zFullName + iStart; if( pTree->pLast ){ pTree->pLast->pNext = pNew; }else{ pTree->pFirst = pNew; } pTree->pLast = pNew; pNew->pParent = pParent; if( pParent ){ if( pParent->pChild ){ pParent->pLastChild->pSibling = pNew; }else{ pParent->pChild = pNew; } pNew->iLevel = pParent->iLevel + 1; pParent->pLastChild = pNew; }else{ if( pTree->pLastTop ) pTree->pLastTop->pSibling = pNew; pTree->pLastTop = pNew; } pNew->mtime = mtime; while( zPath[i]=='/' ){ i++; } pParent = pNew; } while( pParent && pParent->pParent ){ if( pParent->pParent->mtime < pParent->mtime ){ pParent->pParent->mtime = pParent->mtime; } pParent = pParent->pParent; } } /* Comparison function for two FileTreeNode objects. Sort first by ** mtime (larger numbers first) and then by zName (smaller names first). ** ** Return negative if pLeft<pRight. ** Return positive if pLeft>pRight. ** Return zero if pLeft==pRight. */ static int compareNodes(FileTreeNode *pLeft, FileTreeNode *pRight){ if( pLeft->mtime>pRight->mtime ) return -1; if( pLeft->mtime<pRight->mtime ) return +1; return fossil_stricmp(pLeft->zName, pRight->zName); } /* Merge together two sorted lists of FileTreeNode objects */ static FileTreeNode *mergeNodes(FileTreeNode *pLeft, FileTreeNode *pRight){ FileTreeNode *pEnd; FileTreeNode base; pEnd = &base; while( pLeft && pRight ){ if( compareNodes(pLeft,pRight)<=0 ){ pEnd = pEnd->pSibling = pLeft; pLeft = pLeft->pSibling; }else{ pEnd = pEnd->pSibling = pRight; pRight = pRight->pSibling; } } if( pLeft ){ pEnd->pSibling = pLeft; }else{ pEnd->pSibling = pRight; } return base.pSibling; } /* Sort a list of FileTreeNode objects in mtime order. */ static FileTreeNode *sortNodesByMtime(FileTreeNode *p){ FileTreeNode *a[30]; FileTreeNode *pX; int i; memset(a, 0, sizeof(a)); while( p ){ pX = p; p = pX->pSibling; pX->pSibling = 0; for(i=0; i<count(a)-1 && a[i]!=0; i++){ pX = mergeNodes(a[i], pX); a[i] = 0; } a[i] = mergeNodes(a[i], pX); } pX = 0; for(i=0; i<count(a); i++){ pX = mergeNodes(a[i], pX); } return pX; } /* Sort an entire FileTreeNode tree by mtime ** ** This routine invalidates the following fields: ** ** FileTreeNode.pLastChild ** FileTreeNode.pNext ** ** Use relinkTree to reconnect the pNext pointers. */ static FileTreeNode *sortTreeByMtime(FileTreeNode *p){ FileTreeNode *pX; for(pX=p; pX; pX=pX->pSibling){ if( pX->pChild ) pX->pChild = sortTreeByMtime(pX->pChild); } return sortNodesByMtime(p); } /* Reconstruct the FileTree by reconnecting the FileTreeNode.pNext ** fields in sequential order. */ static void relinkTree(FileTree *pTree, FileTreeNode *pRoot){ while( pRoot ){ if( pTree->pLast ){ pTree->pLast->pNext = pRoot; }else{ pTree->pFirst = pRoot; } pTree->pLast = pRoot; if( pRoot->pChild ) relinkTree(pTree, pRoot->pChild); pRoot = pRoot->pSibling; } if( pTree->pLast ) pTree->pLast->pNext = 0; } /* ** WEBPAGE: tree ** ** Query parameters: ** ** name=PATH Directory to display. Optional ** ci=LABEL Show only files in this check-in. Optional. ** re=REGEXP Show only files matching REGEXP. Optional. ** expand Begin with the tree fully expanded. ** nofiles Show directories (folders) only. Omit files. ** mtime Order directory elements by decreasing mtime */ void page_tree(void){ char *zD = fossil_strdup(P("name")); int nD = zD ? strlen(zD)+1 : 0; const char *zCI = P("ci"); int rid = 0; char *zUuid = 0; Blob dirname; Manifest *pM = 0; double rNow = 0; char *zNow = 0; int useMtime = atoi(PD("mtime","0")); int nFile = 0; /* Number of files (or folders with "nofiles") */ int linkTrunk = 1; /* include link to "trunk" */ int linkTip = 1; /* include link to "tip" */ const char *zRE; /* the value for the re=REGEXP query parameter */ const char *zObjType; /* "files" by default or "folders" for "nofiles" */ char *zREx = ""; /* Extra parameters for path hyperlinks */ ReCompiled *pRE = 0; /* Compiled regular expression */ FileTreeNode *p; /* One line of the tree */ FileTree sTree; /* The complete tree of files */ HQuery sURI; /* Hyperlink */ int startExpanded; /* True to start out with the tree expanded */ int showDirOnly; /* Show directories only. Omit files */ int nDir = 0; /* Number of directories. Used for ID attributes */ char *zProjectName = db_get("project-name", 0); if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; } memset(&sTree, 0, sizeof(sTree)); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, pathelementFunc, 0, 0); url_initialize(&sURI, "tree"); cgi_query_parameters_to_url(&sURI); if( PB("nofiles") ){ showDirOnly = 1; style_header("Folder Hierarchy"); }else{ showDirOnly = 0; style_header("File Tree"); } style_adunit_config(ADUNIT_RIGHT_OK); if( PB("expand") ){ startExpanded = 1; }else{ startExpanded = 0; } /* If a regular expression is specified, compile it */ zRE = P("re"); if( zRE ){ re_compile(&pRE, zRE, 0); zREx = mprintf("&re=%T", zRE); } /* If the name= parameter is an empty string, make it a NULL pointer */ if( zD && strlen(zD)==0 ){ zD = 0; } /* If a specific check-in is requested, fetch and parse it. If the ** specific check-in does not exist, clear zCI. zCI==0 will cause all ** files from all check-ins to be displayed. */ if( zCI ){ pM = manifest_get_by_name(zCI, &rid); if( pM ){ int trunkRid = symbolic_name_to_rid("tag:trunk", "ci"); linkTrunk = trunkRid && rid != trunkRid; linkTip = rid != symbolic_name_to_rid("tip", "ci"); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); zNow = db_text("", "SELECT datetime(mtime,'localtime')" " FROM event WHERE objid=%d", rid); }else{ zCI = 0; } } if( zCI==0 ){ rNow = db_double(0.0, "SELECT max(mtime) FROM event"); zNow = db_text("", "SELECT datetime(max(mtime),'localtime') FROM event"); } /* Compute the title of the page */ blob_zero(&dirname); if( zD ){ blob_append(&dirname, "within directory ", -1); hyperlinked_path(zD, &dirname, zCI, "tree", zREx); if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE); style_submenu_element("Top-Level", "Top-Level", "%s", url_render(&sURI, "name", 0, 0, 0)); }else{ if( zRE ){ blob_appendf(&dirname, "matching \"%s\"", zRE); } } style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0); if( zCI ){ style_submenu_element("All", "All", "%s", url_render(&sURI, "ci", 0, 0, 0)); if( nD==0 && !showDirOnly ){ style_submenu_element("File Ages", "File Ages", "%R/fileage?name=%s", zUuid); } } if( linkTrunk ){ style_submenu_element("Trunk", "Trunk", "%s", url_render(&sURI, "ci", "trunk", 0, 0)); } if( linkTip ){ style_submenu_element("Tip", "Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0)); } style_submenu_element("Flat-View", "Flat-View", "%s", url_render(&sURI, "type", "flat", 0, 0)); /* Compute the file hierarchy. */ if( zCI ){ Stmt q; compute_fileage(rid, 0); db_prepare(&q, "SELECT filename.name, blob.uuid, fileage.mtime\n" " FROM fileage, filename, blob\n" " WHERE filename.fnid=fileage.fnid\n" " AND blob.rid=fileage.fid\n" " ORDER BY filename.name COLLATE nocase;" ); while( db_step(&q)==SQLITE_ROW ){ const char *zFile = db_column_text(&q,0); const char *zUuid = db_column_text(&q,1); double mtime = db_column_double(&q,2); if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){ continue; } if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; tree_add_node(&sTree, zFile, zUuid, mtime); nFile++; } db_finalize(&q); }else{ Stmt q; db_prepare(&q, "SELECT filename.name, blob.uuid, max(event.mtime)\n" " FROM filename, mlink, blob, event\n" " WHERE mlink.fnid=filename.fnid\n" " AND event.objid=mlink.mid\n" " AND blob.rid=mlink.fid\n" " GROUP BY 1 ORDER BY 1 COLLATE nocase"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zUuid = db_column_text(&q,1); double mtime = db_column_double(&q,2); if( nD>0 && (fossil_strncmp(zName, zD, nD-1)!=0 || zName[nD-1]!='/') ){ continue; } if( pRE && re_match(pRE, (const u8*)zName, -1)==0 ) continue; tree_add_node(&sTree, zName, zUuid, mtime); nFile++; } db_finalize(&q); } if( showDirOnly ){ for(nFile=0, p=sTree.pFirst; p; p=p->pNext){ if( p->pChild!=0 && p->nFullName>nD ) nFile++; } zObjType = "Folders"; style_submenu_element("Files","Files","%s", url_render(&sURI,"nofiles",0,0,0)); }else{ zObjType = "Files"; style_submenu_element("Folders","Folders","%s", url_render(&sURI,"nofiles","1",0,0)); } if( zCI ){ @ <h2>%s(zObjType) from if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){ @ "%h(zCI)" } @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname)) }else{ int n = db_int(0, "SELECT count(*) FROM plink"); @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname)) } if( useMtime ){ @ sorted by modification time</h2> }else{ @ sorted by filename</h2> } /* Generate tree of lists. ** ** Each file and directory is a list element: <li>. Files have class=file ** and if the filename as the suffix "xyz" the file also has class=file-xyz. ** Directories have class=dir. The directory specfied by the name= query ** parameter (or the top-level directory if there is no name= query parameter) ** adds class=subdir. ** ** The <li> element for directories also contains a sublist <ul> ** for the contents of that directory. */ @ <div class="filetree"><ul> if( nD ){ @ <li class="dir last"> }else{ @ <li class="dir subdir last"> } @ <div class="filetreeline"> @ %z(href("%s",url_render(&sURI,"name",0,0,0)))%h(zProjectName)</a> if( zNow ){ @ <div class="filetreeage">%s(zNow)</div> } @ </div> @ <ul> if( useMtime ){ p = sortTreeByMtime(sTree.pFirst); memset(&sTree, 0, sizeof(sTree)); relinkTree(&sTree, p); } for(p=sTree.pFirst, nDir=0; p; p=p->pNext){ const char *zLastClass = p->pSibling==0 ? " last" : ""; if( p->pChild ){ const char *zSubdirClass = p->nFullName==nD-1 ? " subdir" : ""; @ <li class="dir%s(zSubdirClass)%s(zLastClass)"><div class="filetreeline"> @ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a> if( p->mtime>0.0 ){ char *zAge = human_readable_age(rNow - p->mtime); @ <div class="filetreeage">%s(zAge)</div> } @ </div> if( startExpanded || p->nFullName<=nD ){ @ <ul id="dir%d(nDir)"> }else{ @ <ul id="dir%d(nDir)" class="collapsed"> } nDir++; }else if( !showDirOnly ){ const char *zFileClass = fileext_class(p->zName); char *zLink; if( zCI ){ zLink = href("%R/artifact/%!S",p->zUuid); }else{ zLink = href("%R/finfo?name=%T",p->zFullName); } @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline"> @ %z(zLink)%h(p->zName)</a> if( p->mtime>0 ){ char *zAge = human_readable_age(rNow - p->mtime); @ <div class="filetreeage">%s(zAge)</div> } @ </div> } if( p->pSibling==0 ){ int nClose = p->iLevel - (p->pNext ? p->pNext->iLevel : 0); while( nClose-- > 0 ){ @ </ul> } } } @ </ul> @ </ul></div> @ <script>(function(){ @ function isExpanded(ul){ @ return ul.className==''; @ } @ @ function toggleDir(ul, useInitValue){ @ if( !useInitValue ){ @ expandMap[ul.id] = !isExpanded(ul); @ history.replaceState(expandMap, ''); @ } @ ul.className = expandMap[ul.id] ? '' : 'collapsed'; @ } @ @ function toggleAll(tree, useInitValue){ @ var lists = tree.querySelectorAll('.subdir > ul > li ul'); @ if( !useInitValue ){ @ var expand = true; /* Default action: make all sublists visible */ @ for( var i=0; lists[i]; i++ ){ @ if( isExpanded(lists[i]) ){ @ expand = false; /* Any already visible - make them all hidden */ @ break; @ } @ } @ expandMap = {'*': expand}; @ history.replaceState(expandMap, ''); @ } @ var className = expandMap['*'] ? '' : 'collapsed'; @ for( var i=0; lists[i]; i++ ){ @ lists[i].className = className; @ } @ } @ @ function checkState(){ @ expandMap = history.state || {}; @ if( '*' in expandMap ) toggleAll(outer_ul, true); @ for( var id in expandMap ){ @ if( id!=='*' ) toggleDir(gebi(id), true); @ } @ } @ @ function belowSubdir(node){ @ do{ @ node = node.parentNode; @ if( node==subdir ) return true; @ } while( node && node!=outer_ul ); @ return false; @ } @ @ var history = window.history || {}; @ if( !history.replaceState ) history.replaceState = function(){}; @ var outer_ul = document.querySelector('.filetree > ul'); @ var subdir = outer_ul.querySelector('.subdir'); @ var expandMap = {}; @ checkState(); @ outer_ul.onclick = function(e){ @ e = e || window.event; @ var a = e.target || e.srcElement; @ if( a.nodeName!='A' ) return true; @ if( a.parentNode.parentNode==subdir ){ @ toggleAll(outer_ul); @ return false; @ } @ if( !belowSubdir(a) ) return true; @ var ul = a.parentNode.nextSibling; @ while( ul && ul.nodeName!='UL' ) ul = ul.nextSibling; @ if( !ul ) return true; /* This is a file link, not a directory */ @ toggleDir(ul); @ return false; @ } @ }())</script> style_footer(); /* We could free memory used by sTree here if we needed to. But ** the process is about to exit, so doing so would not really accomplish ** anything useful. */ } /* ** Return a CSS class name based on the given filename's extension. ** Result must be freed by the caller. **/ const char *fileext_class(const char *zFilename){ char *zClass; const char *zExt = strrchr(zFilename, '.'); int isExt = zExt && zExt!=zFilename && zExt[1]; int i; for( i=1; isExt && zExt[i]; i++ ) isExt &= fossil_isalnum(zExt[i]); if( isExt ){ zClass = mprintf("file file-%s", zExt+1); for( i=5; zClass[i]; i++ ) zClass[i] = fossil_tolower(zClass[i]); }else{ zClass = mprintf("file"); } return zClass; } /* ** SQL used to compute the age of all files in check-in :ckin whose ** names match :glob */ static const char zComputeFileAgeSetup[] = @ CREATE TABLE IF NOT EXISTS temp.fileage( @ fnid INTEGER PRIMARY KEY, @ fid INTEGER, @ mid INTEGER, @ mtime DATETIME, @ pathname TEXT @ ); @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; ; static const char zComputeFileAgeRun[] = @ WITH RECURSIVE @ ckin(x,m) AS (SELECT objid, mtime FROM event WHERE objid=:ckin @ UNION @ SELECT plink.pid, event.mtime @ FROM ckin, plink, event @ WHERE plink.cid=ckin.x AND event.objid=plink.pid @ ORDER BY 2 DESC) @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname) @ SELECT filename.fnid, mlink.fid, mlink.mid, event.mtime, filename.name @ FROM foci, filename, blob, mlink, event @ WHERE foci.checkinID=:ckin @ AND foci.filename GLOB :glob @ AND filename.name=foci.filename @ AND blob.uuid=foci.uuid @ AND mlink.fid=blob.rid @ AND mlink.fid!=mlink.pid @ AND mlink.mid IN (SELECT x FROM ckin) @ AND event.objid=mlink.mid @ ORDER BY event.mtime ASC; ; /* ** Look at all file containing in the version "vid". Construct a ** temporary table named "fileage" that contains the file-id for each ** files, the pathname, the check-in where the file was added, and the ** mtime on that check-in. If zGlob and *zGlob then only files matching ** the given glob are computed. */ int compute_fileage(int vid, const char* zGlob){ Stmt q; db_multi_exec(zComputeFileAgeSetup /*works-like:"constant"*/); db_prepare(&q, zComputeFileAgeRun /*works-like:"constant"*/); db_bind_int(&q, ":ckin", vid); db_bind_text(&q, ":glob", zGlob && zGlob[0] ? zGlob : "*"); db_exec(&q); db_finalize(&q); return 0; } /* ** Render the number of days in rAge as a more human-readable time span. ** Different units (seconds, minutes, hours, days, months, years) are ** selected depending on the magnitude of rAge. ** ** The string returned is obtained from fossil_malloc() and should be ** freed by the caller. */ char *human_readable_age(double rAge){ if( rAge*86400.0<120 ){ if( rAge*86400.0<1.0 ){ return mprintf("current"); }else{ return mprintf("%d seconds", (int)(rAge*86400.0)); } }else if( rAge*1440.0<90 ){ return mprintf("%.1f minutes", rAge*1440.0); }else if( rAge*24.0<36 ){ return mprintf("%.1f hours", rAge*24.0); }else if( rAge<365.0 ){ return mprintf("%.1f days", rAge); }else{ return mprintf("%.2f years", rAge/365.0); } } /* ** COMMAND: test-fileage ** ** Usage: %fossil test-fileage CHECKIN */ void test_fileage_cmd(void){ int mid; Stmt q; const char *zGlob = find_option("glob",0,1); db_find_and_open_repository(0,0); verify_all_options(); if( g.argc!=3 ) usage("test-fileage CHECKIN"); mid = name_to_typed_rid(g.argv[2],"ci"); compute_fileage(mid, zGlob); db_prepare(&q, "SELECT fid, mid, julianday('now') - mtime, pathname" " FROM fileage" ); while( db_step(&q)==SQLITE_ROW ){ char *zAge = human_readable_age(db_column_double(&q,2)); fossil_print("%8d %8d %16s %s\n", db_column_int(&q,0), db_column_int(&q,1), zAge, db_column_text(&q,3)); fossil_free(zAge); } db_finalize(&q); } /* ** WEBPAGE: fileage ** ** Parameters: ** name=VERSION Selects the check-in version (default=tip). ** glob=STRING Only shows files matching this glob pattern ** (e.g. *.c or *.txt). ** showid Show RID values for debugging */ void fileage_page(void){ int rid; const char *zName; const char *zGlob; const char *zUuid; const char *zNow; /* Time of check-in */ int showId = PB("showid"); Stmt q1, q2; double baseTime; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } zName = P("name"); if( zName==0 ) zName = "tip"; rid = symbolic_name_to_rid(zName, "ci"); if( rid==0 ){ fossil_fatal("not a valid check-in: %s", zName); } zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid); zNow = db_text("", "SELECT datetime(mtime,'localtime') FROM event" " WHERE objid=%d", rid); style_submenu_element("Tree-View", "Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName); style_header("File Ages"); zGlob = P("glob"); compute_fileage(rid,zGlob); db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); @ <h2>Files in @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a> if( zGlob && zGlob[0] ){ @ that match "%h(zGlob)" and } @ ordered by check-in time</h2> @ @ <p>Times are relative to the check-in time for @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> which is @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p> @ @ <div class='fileage'><table> @ <tr><th>Time</th><th>Files</th><th>Check-in</th></tr> db_prepare(&q1, "SELECT event.mtime, event.objid, blob.uuid,\n" " coalesce(event.ecomment,event.comment),\n" " coalesce(event.euser,event.user),\n" " coalesce((SELECT value FROM tagxref\n" " WHERE tagtype>0 AND tagid=%d\n" " AND rid=event.objid),'trunk')\n" " FROM event, blob\n" " WHERE event.objid IN (SELECT mid FROM fileage)\n" " AND blob.rid=event.objid\n" " ORDER BY event.mtime DESC;", TAG_BRANCH ); db_prepare(&q2, "SELECT blob.uuid, filename.name, fileage.fid\n" " FROM fileage, blob, filename\n" " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid" " AND blob.rid=fileage.fid;" ); while( db_step(&q1)==SQLITE_ROW ){ double age = baseTime - db_column_double(&q1, 0); int mid = db_column_int(&q1, 1); const char *zUuid = db_column_text(&q1, 2); const char *zComment = db_column_text(&q1, 3); const char *zUser = db_column_text(&q1, 4); const char *zBranch = db_column_text(&q1, 5); char *zAge = human_readable_age(age); @ <tr><td>%s(zAge)</td> @ <td> db_bind_int(&q2, ":mid", mid); while( db_step(&q2)==SQLITE_ROW ){ const char *zFUuid = db_column_text(&q2,0); const char *zFile = db_column_text(&q2,1); int fid = db_column_int(&q2,2); if( showId ){ @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br> }else{ @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br> } } db_reset(&q2); @ </td> @ <td> @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a> if( showId ){ @ (%d(mid)) } @ %W(zComment) (user: @ %z(href("%R/timeline?u=%t&c=%!S&nd&n=200",zUser,zUuid))%h(zUser)</a>, @ branch: @ %z(href("%R/timeline?r=%t&c=%!S&nd&n=200",zBranch,zUuid))%h(zBranch)</a>) @ </td></tr> @ fossil_free(zAge); } @ </table></div> db_finalize(&q1); db_finalize(&q2); style_footer(); } |
Added src/builtin.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains built-in string and BLOB resources packaged as ** byte arrays. */ #include "config.h" #include "builtin.h" #include <assert.h> /* ** The resources provided by this file are packaged by the "mkbuiltin.c" ** utility program during the built process and stored in the ** builtin_data.h file. Include that information here: */ #include "builtin_data.h" /* ** Return a pointer to built-in content */ const unsigned char *builtin_file(const char *zFilename, int *piSize){ int lwr, upr, i, c; lwr = 0; upr = sizeof(aBuiltinFiles)/sizeof(aBuiltinFiles[0]) - 1; while( upr>=lwr ){ i = (upr+lwr)/2; c = strcmp(aBuiltinFiles[i].zName,zFilename); if( c<0 ){ lwr = i+1; }else if( c>0 ){ upr = i-1; }else{ if( piSize ) *piSize = aBuiltinFiles[i].nByte; return aBuiltinFiles[i].pData; } } if( piSize ) *piSize = 0; return 0; } const char *builtin_text(const char *zFilename){ return (char*)builtin_file(zFilename, 0); } /* ** COMMAND: test-builtin-list ** ** List the names and sizes of all built-in resources */ void test_builtin_list(void){ int i; for(i=0; i<sizeof(aBuiltinFiles)/sizeof(aBuiltinFiles[0]); i++){ fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte); } } /* ** COMMAND: test-builtin-get ** ** Usage: %fossil test-builtin-get NAME ?OUTPUT-FILE? */ void test_builtin_get(void){ const unsigned char *pData; int nByte; Blob x; if( g.argc!=3 && g.argc!=4 ){ usage("NAME ?OUTPUT-FILE?"); } pData = builtin_file(g.argv[2], &nByte); if( pData==0 ){ fossil_fatal("no such built-in file: [%s]", g.argv[2]); } blob_init(&x, (const char*)pData, nByte); blob_write_to_file(&x, g.argc==4 ? g.argv[3] : "-"); blob_reset(&x); } |
Added src/bundle.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to implement and manage a "bundle" file. */ #include "config.h" #include "bundle.h" #include <assert.h> /* ** SQL code used to initialize the schema of a bundle. ** ** The bblob.delta field can be an integer, a text string, or NULL. ** If an integer, then the corresponding blobid is the delta basis. ** If a text string, then that string is a SHA1 hash for the delta ** basis, which is presumably in the master repository. If NULL, then ** data contains contain without delta compression. */ static const char zBundleInit[] = @ CREATE TABLE IF NOT EXISTS "%w".bconfig( @ bcname TEXT, @ bcvalue ANY @ ); @ CREATE TABLE IF NOT EXISTS "%w".bblob( @ blobid INTEGER PRIMARY KEY, -- Blob ID @ uuid TEXT NOT NULL, -- SHA1 hash of expanded blob @ sz INT NOT NULL, -- Size of blob after expansion @ delta ANY, -- Delta compression basis, or NULL @ notes TEXT, -- Description of content @ data BLOB -- compressed content @ ); ; /* ** Attach a bundle file to the current database connection using the ** attachment name zBName. */ static void bundle_attach_file( const char *zFile, /* Name of the file that contains the bundle */ const char *zBName, /* Attachment name */ int doInit /* Initialize a new bundle, if true */ ){ int rc; char *zErrMsg = 0; char *zSql; if( !doInit && file_size(zFile)<0 ){ fossil_fatal("no such file: %s", zFile); } assert( g.db ); zSql = sqlite3_mprintf("ATTACH %Q AS %Q", zFile, zBName); if( zSql==0 ) fossil_fatal("out of memory"); rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg); sqlite3_free(zSql); if( rc!=SQLITE_OK || zErrMsg ){ if( zErrMsg==0 ) zErrMsg = (char*)sqlite3_errmsg(g.db); fossil_fatal("not a valid bundle: %s", zFile); } if( doInit ){ db_multi_exec(zBundleInit /*works-like:"%w%w"*/, zBName, zBName); }else{ sqlite3_stmt *pStmt; zSql = sqlite3_mprintf("SELECT bcname, bcvalue" " FROM \"%w\".bconfig", zBName); if( zSql==0 ) fossil_fatal("out of memory"); rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0); if( rc ) fossil_fatal("not a valid bundle: %s", zFile); sqlite3_free(zSql); sqlite3_finalize(pStmt); zSql = sqlite3_mprintf("SELECT blobid, uuid, sz, delta, notes, data" " FROM \"%w\".bblob", zBName); if( zSql==0 ) fossil_fatal("out of memory"); rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0); if( rc ) fossil_fatal("not a valid bundle: %s", zFile); sqlite3_free(zSql); sqlite3_finalize(pStmt); } } /* ** fossil bundle ls BUNDLE ?OPTIONS? ** ** Display the content of a bundle in human-readable form. */ static void bundle_ls_cmd(void){ Stmt q; sqlite3_int64 sumSz = 0; sqlite3_int64 sumLen = 0; int bDetails = find_option("details","l",0)!=0; verify_all_options(); if( g.argc!=4 ) usage("ls BUNDLE ?OPTIONS?"); bundle_attach_file(g.argv[3], "b1", 0); db_prepare(&q, "SELECT bcname, bcvalue FROM bconfig" " WHERE typeof(bcvalue)='text'" " AND bcvalue NOT GLOB char(0x2a,0x0a,0x2a);" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%s: %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); fossil_print("%.78c\n",'-'); if( bDetails ){ db_prepare(&q, "SELECT blobid, substr(uuid,1,10), coalesce(substr(delta,1,10),'')," " sz, length(data), notes" " FROM bblob" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%4d %10s %10s %8d %8d %s\n", db_column_int(&q,0), db_column_text(&q,1), db_column_text(&q,2), db_column_int(&q,3), db_column_int(&q,4), db_column_text(&q,5)); sumSz += db_column_int(&q,3); sumLen += db_column_int(&q,4); } db_finalize(&q); fossil_print("%27s %8lld %8lld\n", "Total:", sumSz, sumLen); }else{ db_prepare(&q, "SELECT substr(uuid,1,16), notes FROM bblob" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%16s %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); } } /* ** Implement the "fossil bundle append BUNDLE FILE..." command. Add ** the named files into the BUNDLE. Create the BUNDLE if it does not ** alraedy exist. */ static void bundle_append_cmd(void){ Blob content, hash; int i; Stmt q; verify_all_options(); bundle_attach_file(g.argv[3], "b1", 1); db_prepare(&q, "INSERT INTO bblob(blobid, uuid, sz, delta, data, notes) " "VALUES(NULL, $uuid, $sz, NULL, $data, $filename)"); db_begin_transaction(); for(i=4; i<g.argc; i++){ int sz; blob_read_from_file(&content, g.argv[i]); sz = blob_size(&content); sha1sum_blob(&content, &hash); blob_compress(&content, &content); db_bind_text(&q, "$uuid", blob_str(&hash)); db_bind_int(&q, "$sz", sz); db_bind_blob(&q, "$data", &content); db_bind_text(&q, "$filename", g.argv[i]); db_step(&q); db_reset(&q); blob_reset(&content); blob_reset(&hash); } db_end_transaction(0); db_finalize(&q); } /* ** Identify a subsection of the check-in tree using command-line switches. ** There must be one of the following switch available: ** ** --branch BRANCHNAME All check-ins on the most recent ** instance of BRANCHNAME ** --from TAG1 [--to TAG2] Check-in TAG1 and all primary descendants ** up to and including TAG2 ** --checkin TAG Check-in TAG only ** ** Store the RIDs for all applicable check-ins in the zTab table that ** should already exist. Invoke fossil_fatal() if any kind of error is ** seen. */ void subtree_from_arguments(const char *zTab){ const char *zBr; const char *zFrom; const char *zTo; const char *zCkin; int rid = 0, endRid; zBr = find_option("branch",0,1); zFrom = find_option("from",0,1); zTo = find_option("to",0,1); zCkin = find_option("checkin",0,1); if( zCkin ){ if( zFrom ) fossil_fatal("cannot use both --checkin and --from"); if( zBr ) fossil_fatal("cannot use both --checkin and --branch"); rid = symbolic_name_to_rid(zCkin, "ci"); endRid = rid; }else{ endRid = zTo ? name_to_typed_rid(zTo, "ci") : 0; } if( zFrom ){ rid = name_to_typed_rid(zFrom, "ci"); }else if( zBr ){ rid = name_to_typed_rid(zBr, "br"); }else if( zCkin==0 ){ fossil_fatal("need one of: --branch, --from, --checkin"); } db_multi_exec("INSERT OR IGNORE INTO \"%w\" VALUES(%d)", zTab, rid); if( rid!=endRid ){ Blob sql; blob_zero(&sql); blob_appendf(&sql, "WITH RECURSIVE child(rid) AS (VALUES(%d) UNION ALL " " SELECT cid FROM plink, child" " WHERE plink.pid=child.rid" " AND plink.isPrim", rid); if( endRid>0 ){ double endTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", endRid); blob_appendf(&sql, " AND child.rid!=%d" " AND (SELECT mtime FROM event WHERE objid=plink.cid)<=%.17g", endRid, endTime ); } if( zBr ){ blob_appendf(&sql, " AND EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0" " AND value=%Q and rid=plink.cid)", TAG_BRANCH, zBr); } blob_appendf(&sql, ") INSERT OR IGNORE INTO \"%w\" SELECT rid FROM child;", zTab); db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/); } } /* ** COMMAND: test-subtree ** ** Usage: %fossil test-subtree ?OPTIONS? ** ** Show the subset of check-ins that match the supplied options. This ** command is used to test the subtree_from_options() subroutine in the ** implementation and does not really have any other practical use that ** we know of. ** ** Options: ** --branch BRANCH Include only check-ins on BRANCH ** --from TAG Start the subtree at TAG ** --to TAG End the subtree at TAG ** --checkin TAG The subtree is the single check-in TAG ** --all Include FILE and TAG artifacts ** --exclusive Include FILES exclusively on check-ins */ void test_subtree_cmd(void){ int bAll = find_option("all",0,0)!=0; int bExcl = find_option("exclusive",0,0)!=0; db_find_and_open_repository(0,0); db_begin_transaction(); db_multi_exec("CREATE TEMP TABLE tobundle(rid INTEGER PRIMARY KEY);"); subtree_from_arguments("tobundle"); verify_all_options(); if( bAll ) find_checkin_associates("tobundle",bExcl); describe_artifacts_to_stdout("IN tobundle", 0); db_end_transaction(1); } /* fossil bundle export BUNDLE ?OPTIONS? ** ** OPTIONS: ** --branch BRANCH --from TAG --to TAG ** --checkin TAG ** --standalone */ static void bundle_export_cmd(void){ int bStandalone = find_option("standalone",0,0)!=0; int mnToBundle; /* Minimum RID in the bundle */ Stmt q; /* Decode the arguments (like --branch) that specify which artifacts ** should be in the bundle */ db_multi_exec("CREATE TEMP TABLE tobundle(rid INTEGER PRIMARY KEY);"); subtree_from_arguments("tobundle"); find_checkin_associates("tobundle", 0); verify_all_options(); describe_artifacts("IN tobundle"); if( g.argc!=4 ) usage("export BUNDLE ?OPTIONS?"); /* Create the new bundle */ bundle_attach_file(g.argv[3], "b1", 1); db_begin_transaction(); /* Add 'mtime' and 'project-code' entries to the bconfig table */ db_multi_exec( "INSERT INTO bconfig(bcname,bcvalue)" " VALUES('mtime',datetime('now'));" ); db_multi_exec( "INSERT INTO bconfig(bcname,bcvalue)" " SELECT name, value FROM config" " WHERE name IN ('project-code');" ); /* Directly copy content from the repository into the bundle as long ** as the repository content is a delta from some other artifact that ** is also in the bundle. */ db_multi_exec( "REPLACE INTO bblob(blobid,uuid,sz,delta,data,notes) " " SELECT" " tobundle.rid," " blob.uuid," " blob.size," " delta.srcid," " blob.content," " (SELECT summary FROM description WHERE rid=blob.rid)" " FROM tobundle, blob, delta" " WHERE blob.rid=tobundle.rid" " AND delta.rid=tobundle.rid" " AND delta.srcid IN tobundle;" ); /* For all the remaining artifacts, we need to construct their deltas ** manually. */ mnToBundle = db_int(0,"SELECT min(rid) FROM tobundle"); db_prepare(&q, "SELECT rid FROM tobundle" " WHERE rid NOT IN (SELECT blobid FROM bblob)" " ORDER BY +rid;" ); while( db_step(&q)==SQLITE_ROW ){ Blob content; int rid = db_column_int(&q,0); int deltaFrom = 0; /* Get the raw, uncompressed content of the artifact into content */ content_get(rid, &content); /* Try to find another artifact, not within the bundle, that is a ** plausible candidate for being a delta basis for the content. Set ** deltaFrom to the RID of that other artifact. Leave deltaFrom set ** to zero if the content should not be delta-compressed */ if( !bStandalone ){ if( db_exists("SELECT 1 FROM plink WHERE cid=%d",rid) ){ deltaFrom = db_int(0, "SELECT max(cid) FROM plink" " WHERE cid<%d", mnToBundle); }else{ deltaFrom = db_int(0, "SELECT max(fid) FROM mlink" " WHERE fnid=(SELECT fnid FROM mlink WHERE fid=%d)" " AND fid<%d", rid, mnToBundle); } } /* Try to insert the insert the artifact as a delta */ if( deltaFrom ){ Blob basis, delta; content_get(deltaFrom, &basis); blob_delta_create(&basis, &content, &delta); if( blob_size(&delta)>0.9*blob_size(&content) ){ deltaFrom = 0; }else{ Stmt ins; blob_compress(&delta, &delta); db_prepare(&ins, "REPLACE INTO bblob(blobid,uuid,sz,delta,data,notes)" " SELECT %d, uuid, size, (SELECT uuid FROM blob WHERE rid=%d)," " :delta, (SELECT summary FROM description WHERE rid=blob.rid)" " FROM blob WHERE rid=%d", rid, deltaFrom, rid); db_bind_blob(&ins, ":delta", &delta); db_step(&ins); db_finalize(&ins); } blob_reset(&basis); blob_reset(&delta); } /* If unable to insert the artifact as a delta, insert full-text */ if( deltaFrom==0 ){ Stmt ins; blob_compress(&content, &content); db_prepare(&ins, "REPLACE INTO bblob(blobid,uuid,sz,delta,data,notes)" " SELECT rid, uuid, size, NULL, :content," " (SELECT summary FROM description WHERE rid=blob.rid)" " FROM blob WHERE rid=%d", rid); db_bind_blob(&ins, ":content", &content); db_step(&ins); db_finalize(&ins); } blob_reset(&content); } db_finalize(&q); db_end_transaction(0); } /* ** There is a TEMP table bix(blobid,delta) containing a set of purgeitems ** that need to be transferred to the BLOB table. This routine does ** all items that have srcid=iSrc. The pBasis blob holds the content ** of the source document if iSrc>0. */ static void bundle_import_elements(int iSrc, Blob *pBasis, int isPriv){ Stmt q; static Bag busy; assert( pBasis!=0 || iSrc==0 ); if( iSrc>0 ){ if( bag_find(&busy, iSrc) ){ fossil_fatal("delta loop while uncompressing bundle artifacts"); } bag_insert(&busy, iSrc); } db_prepare(&q, "SELECT uuid, data, bblob.delta, bix.blobid" " FROM bix, bblob" " WHERE bix.delta=%d" " AND bix.blobid=bblob.blobid;", iSrc ); while( db_step(&q)==SQLITE_ROW ){ Blob h1, h2, c1, c2; int rid; blob_zero(&h1); db_column_blob(&q, 0, &h1); blob_zero(&c1); db_column_blob(&q, 1, &c1); blob_uncompress(&c1, &c1); blob_zero(&c2); if( db_column_type(&q,2)==SQLITE_TEXT && db_column_bytes(&q,2)==40 ){ Blob basis; rid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q", db_column_text(&q,2)); content_get(rid, &basis); blob_delta_apply(&basis, &c1, &c2); blob_reset(&basis); blob_reset(&c1); }else if( pBasis ){ blob_delta_apply(pBasis, &c1, &c2); blob_reset(&c1); }else{ c2 = c1; } sha1sum_blob(&c2, &h2); if( blob_compare(&h1, &h2)!=0 ){ fossil_fatal("SHA1 hash mismatch - wanted %s, got %s", blob_str(&h1), blob_str(&h2)); } blob_reset(&h2); rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv); if( rid==0 ){ fossil_fatal("%s", g.zErrMsg); }else{ if( !isPriv ) content_make_public(rid); content_get(rid, &c1); manifest_crosslink(rid, &c1, MC_NO_ERRORS); db_multi_exec("INSERT INTO got(rid) VALUES(%d)",rid); } bundle_import_elements(db_column_int(&q,3), &c2, isPriv); blob_reset(&c2); } db_finalize(&q); if( iSrc>0 ) bag_remove(&busy, iSrc); } /* ** Extract an item from content from the bundle */ static void bundle_extract_item( int blobid, /* ID of the item to extract */ Blob *pOut /* Write the content into this blob */ ){ Stmt q; Blob x, basis, h1, h2; static Bag busy; db_prepare(&q, "SELECT uuid, delta, data FROM bblob" " WHERE blobid=%d", blobid); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); fossil_fatal("no such item: %d", blobid); } if( bag_find(&busy, blobid) ) fossil_fatal("delta loop"); blob_zero(&x); db_column_blob(&q, 2, &x); blob_uncompress(&x, &x); if( db_column_type(&q,1)==SQLITE_INTEGER ){ bundle_extract_item(db_column_int(&q,1), &basis); blob_delta_apply(&basis, &x, pOut); blob_reset(&basis); blob_reset(&x); }else if( db_column_type(&q,1)==SQLITE_TEXT ){ int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", db_column_text(&q,1)); if( rid==0 ){ fossil_fatal("cannot find delta basis %s", db_column_text(&q,1)); } content_get(rid, &basis); db_column_blob(&q, 2, &x); blob_delta_apply(&basis, &x, pOut); blob_reset(&basis); blob_reset(&x); }else{ *pOut = x; } blob_zero(&h1); db_column_blob(&q, 0, &h1); sha1sum_blob(pOut, &h2); if( blob_compare(&h1, &h2)!=0 ){ fossil_fatal("SHA1 hash mismatch - wanted %s, got %s", blob_str(&h1), blob_str(&h2)); } blob_reset(&h1); blob_reset(&h2); bag_remove(&busy, blobid); db_finalize(&q); } /* fossil bundle cat BUNDLE UUID... ** ** Write elements of a bundle on standard output */ static void bundle_cat_cmd(void){ int i; Blob x; verify_all_options(); if( g.argc<5 ) usage("cat BUNDLE UUID..."); bundle_attach_file(g.argv[3], "b1", 1); blob_zero(&x); for(i=4; i<g.argc; i++){ int blobid = db_int(0,"SELECT blobid FROM bblob WHERE uuid LIKE '%q%%'", g.argv[i]); if( blobid==0 ){ fossil_fatal("no such artifact in bundle: %s", g.argv[i]); } bundle_extract_item(blobid, &x); blob_write_to_file(&x, "-"); blob_reset(&x); } } /* fossil bundle import BUNDLE ?OPTIONS? ** ** Attempt to import the changes contained in BUNDLE. Make the change ** private so that they do not sync. ** ** OPTIONS: ** --force Import even if the project-code does not match ** --publish Imported changes are not private */ static void bundle_import_cmd(void){ int forceFlag = find_option("force","f",0)!=0; int isPriv = find_option("publish",0,0)==0; char *zMissingDeltas; verify_all_options(); if ( g.argc!=4 ) usage("import BUNDLE ?OPTIONS?"); bundle_attach_file(g.argv[3], "b1", 1); /* Only import a bundle that was generated from a repo with the same ** project code, unless the --force flag is true */ if( !forceFlag ){ if( !db_exists("SELECT 1 FROM config, bconfig" " WHERE config.name='project-code'" " AND bconfig.bcname='project-code'" " AND config.value=bconfig.bcvalue;") ){ fossil_fatal("project-code in the bundle does not match the " "repository project code. (override with --force)."); } } /* If the bundle contains deltas with a basis that is external to the ** bundle and those external basis files are missing from the local ** repo, then the delta encodings cannot be decoded and the bundle cannot ** be extracted. */ zMissingDeltas = db_text(0, "SELECT group_concat(substr(delta,1,10),' ')" " FROM bblob" " WHERE typeof(delta)='text' AND length(delta)=40" " AND NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.delta)"); if( zMissingDeltas && zMissingDeltas[0] ){ fossil_fatal("delta basis artifacts not found in repository: %s", zMissingDeltas); } db_begin_transaction(); db_multi_exec( "CREATE TEMP TABLE bix(" " blobid INTEGER PRIMARY KEY," " delta INTEGER" ");" "CREATE INDEX bixdelta ON bix(delta);" "INSERT INTO bix(blobid,delta)" " SELECT blobid," " CASE WHEN typeof(delta)=='integer'" " THEN delta ELSE 0 END" " FROM bblob" " WHERE NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.uuid AND size>=0);" "CREATE TEMP TABLE got(rid INTEGER PRIMARY KEY ON CONFLICT IGNORE);" ); manifest_crosslink_begin(); bundle_import_elements(0, 0, isPriv); manifest_crosslink_end(0); describe_artifacts_to_stdout("IN got", "Imported content:"); db_end_transaction(0); } /* fossil bundle purge BUNDLE ** ** Try to undo a prior "bundle import BUNDLE". ** ** If the --force option is omitted, then this will only work if ** there have been no check-ins or tags added that use the import. ** ** This routine never removes content that is not already in the bundle ** so the bundle serves as a backup. The purge can be undone using ** "fossil bundle import BUNDLE". */ static void bundle_purge_cmd(void){ int bForce = find_option("force",0,0)!=0; int bTest = find_option("test",0,0)!=0; /* Undocumented --test option */ const char *zFile = g.argv[3]; verify_all_options(); if ( g.argc!=4 ) usage("purge BUNDLE ?OPTIONS?"); bundle_attach_file(zFile, "b1", 0); db_begin_transaction(); /* Find all check-ins of the bundle */ db_multi_exec( "CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY);" "INSERT OR IGNORE INTO ok SELECT blob.rid FROM bblob, blob, plink" " WHERE bblob.uuid=blob.uuid" " AND plink.cid=blob.rid;" ); /* Check to see if new check-ins have been committed to check-ins in ** the bundle. Do not allow the purge if that is true and if --force ** is omitted. */ if( !bForce ){ Stmt q; int n = 0; db_prepare(&q, "SELECT cid FROM plink WHERE pid IN ok AND cid NOT IN ok" ); while( db_step(&q)==SQLITE_ROW ){ whatis_rid(db_column_int(&q,0),0); fossil_print("%.78c\n", '-'); n++; } db_finalize(&q); if( n>0 ){ fossil_fatal("check-ins above are derived from check-ins in the bundle."); } } /* Find all files associated with those check-ins that are used ** nowhere else. */ find_checkin_associates("ok", 1); /* Check to see if any associated files are not in the bundle. Issue ** an error if there are any, unless --force is used. */ if( !bForce ){ db_multi_exec( "CREATE TEMP TABLE err1(rid INTEGER PRIMARY KEY);" "INSERT INTO err1 " " SELECT blob.rid FROM ok CROSS JOIN blob" " WHERE blob.rid=ok.rid" " AND blob.uuid NOT IN (SELECT uuid FROM bblob);" ); if( db_changes() ){ describe_artifacts_to_stdout("IN err1", 0); fossil_fatal("artifacts above associated with bundle check-ins " " are not in the bundle"); }else{ db_multi_exec("DROP TABLE err1;"); } } if( bTest ){ describe_artifacts_to_stdout( "IN (SELECT blob.rid FROM ok, blob, bblob" " WHERE blob.rid=ok.rid AND blob.uuid=bblob.uuid)", "Purged artifacts found in the bundle:"); describe_artifacts_to_stdout( "IN (SELECT blob.rid FROM ok, blob" " WHERE blob.rid=ok.rid " " AND blob.uuid NOT IN (SELECT uuid FROM bblob))", "Purged artifacts NOT in the bundle:"); describe_artifacts_to_stdout( "IN (SELECT blob.rid FROM bblob, blob" " WHERE blob.uuid=bblob.uuid " " AND blob.rid NOT IN ok)", "Artifacts in the bundle but not purged:"); }else{ purge_artifact_list("ok",0,0); } db_end_transaction(0); } /* ** COMMAND: bundle ** ** Usage: %fossil bundle SUBCOMMAND ARGS... ** ** fossil bundle append BUNDLE FILE... ** ** Add files named on the command line to BUNDLE. This subcommand has ** little practical use and is mostly intended for testing. ** ** fossil bundle cat BUNDLE UUID... ** ** Extract one or more artifacts from the bundle and write them ** consecutively on standard output. This subcommand was designed ** for testing and introspection of bundles and is not something ** commonly used. ** ** fossil bundle export BUNDLE ?OPTIONS? ** ** Generate a new bundle, in the file named BUNDLE, that contains a ** subset of the check-ins in the repository (usually a single branch) ** described by the --branch, --from, --to, and/or --checkin options, ** at least one of which is required. If BUNDLE already exists, the ** specified content is added to the bundle. ** ** --branch BRANCH Package all check-ins on BRANCH. ** --from TAG1 --to TAG2 Package check-ins between TAG1 and TAG2. ** --checkin TAG Package the single check-in TAG ** --standalone Do no use delta-encoding against ** artifacts not in the bundle ** ** fossil bundle extend BUNDLE ** ** The BUNDLE must already exist. This subcommand adds to the bundle ** any check-ins that are descendants of check-ins already in the bundle, ** and any tags that apply to artifacts in the bundle. ** ** fossil bundle import BUNDLE ?--publish? ** ** Import all content from BUNDLE into the repository. By default, the ** imported files are private and will not sync. Use the --publish ** option makes the import public. ** ** fossil bundle ls BUNDLE ** ** List the contents of BUNDLE on standard output ** ** fossil bundle purge BUNDLE ** ** Remove from the repository all files that are used exclusively ** by check-ins in BUNDLE. This has the effect of undoing a ** "fossil bundle import". ** ** SUMMARY: ** fossil bundle append BUNDLE FILE... Add files to BUNDLE ** fossil bundle cat BUNDLE UUID... Extract file from BUNDLE ** fossil bundle export BUNDLE ?OPTIONS? Create a new BUNDLE ** --branch BRANCH --from TAG1 --to TAG2 Check-ins to include ** --checkin TAG Use only check-in TAG ** --standalone Omit dependencies ** fossil bundle extend BUNDLE Update with newer content ** fossil bundle import BUNDLE ?OPTIONS? Import a bundle ** --publish Publish the import ** --force Cross-repo import ** fossil bundle ls BUNDLE List content of a bundle ** fossil bundle purge BUNDLE Undo an import ** ** See also: publish */ void bundle_cmd(void){ const char *zSubcmd; int n; if( g.argc<4 ) usage("SUBCOMMAND BUNDLE ?OPTIONS?"); zSubcmd = g.argv[2]; db_find_and_open_repository(0,0); n = (int)strlen(zSubcmd); if( strncmp(zSubcmd, "append", n)==0 ){ bundle_append_cmd(); }else if( strncmp(zSubcmd, "cat", n)==0 ){ bundle_cat_cmd(); }else if( strncmp(zSubcmd, "export", n)==0 ){ bundle_export_cmd(); }else if( strncmp(zSubcmd, "extend", n)==0 ){ fossil_fatal("not yet implemented"); }else if( strncmp(zSubcmd, "import", n)==0 ){ bundle_import_cmd(); }else if( strncmp(zSubcmd, "ls", n)==0 ){ bundle_ls_cmd(); }else if( strncmp(zSubcmd, "purge", n)==0 ){ bundle_purge_cmd(); }else{ fossil_fatal("unknown subcommand for bundle: %s", zSubcmd); } } |
Added src/cache.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@sqlite.org ** ******************************************************************************* ** ** This file implements a cache for expense operations such as ** /zip and /tarball. */ #include "config.h" #include <sqlite3.h> #include "cache.h" /* ** Construct the name of the repository cache file */ static char *cacheName(void){ int i; int n; if( g.zRepositoryName==0 ) return 0; n = (int)strlen(g.zRepositoryName); for(i=n-1; i>=0; i--){ if( g.zRepositoryName[i]=='/' ){ i = n; break; } if( g.zRepositoryName[i]=='.' ) break; } if( i<0 ) i = n; return mprintf("%.*s.cache", i, g.zRepositoryName); } /* ** Attempt to open the cache database, if such a database exists. ** Make sure the cache table exists within that database. */ static sqlite3 *cacheOpen(int bForce){ char *zDbName; sqlite3 *db = 0; int rc; i64 sz; zDbName = cacheName(); if( zDbName==0 ) return 0; if( bForce==0 ){ sz = file_size(zDbName); if( sz<=0 ){ fossil_free(zDbName); return 0; } } rc = sqlite3_open(zDbName, &db); fossil_free(zDbName); if( rc ){ sqlite3_close(db); return 0; } rc = sqlite3_exec(db, "PRAGMA page_size=8192;" "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);" "CREATE TABLE IF NOT EXISTS cache(" "key TEXT PRIMARY KEY," /* Key used to access the cache */ "id INT REFERENCES blob," /* The cache content */ "sz INT," /* Size of content in bytes */ "tm INT," /* Last access time (unix timestampe) */ "nref INT" /* Number of uses */ ");" "CREATE TRIGGER IF NOT EXISTS cacheDel AFTER DELETE ON cache BEGIN" " DELETE FROM blob WHERE id=OLD.id;" "END;", 0, 0, 0); if( rc!=SQLITE_OK ){ sqlite3_close(db); return 0; } return db; } /* ** Attempt to construct a prepared statement for the cache database. */ static sqlite3_stmt *cacheStmt(sqlite3 *db, const char *zSql){ sqlite3_stmt *pStmt = 0; int rc; rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc ){ sqlite3_finalize(pStmt); pStmt = 0; } return pStmt; } /* ** This routine implements an SQL function that renders a large integer ** compactly: ex: 12.3MB */ static void cache_sizename( sqlite3_context *context, int argc, sqlite3_value **argv ){ char zBuf[30]; double v, x; assert( argc==1 ); v = sqlite3_value_double(argv[0]); x = v<0.0 ? -v : v; if( x>=1e9 ){ sqlite3_snprintf(sizeof(zBuf), zBuf, "%.1fGB", v/1e9); }else if( x>=1e6 ){ sqlite3_snprintf(sizeof(zBuf), zBuf, "%.1fMB", v/1e6); }else if( x>=1e3 ){ sqlite3_snprintf(sizeof(zBuf), zBuf, "%.1fKB", v/1e3); }else{ sqlite3_snprintf(sizeof(zBuf), zBuf, "%gB", v); } sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); } /* ** Register the sizename() SQL function with the SQLite database ** connection. */ static void cache_register_sizename(sqlite3 *db){ sqlite3_create_function(db, "sizename", 1, SQLITE_UTF8, 0, cache_sizename, 0, 0); } /* ** Attempt to write pContent into the cache. If the cache file does ** not exist, then this routine is a no-op. Older cache entries might ** be deleted. */ void cache_write(Blob *pContent, const char *zKey){ sqlite3 *db; sqlite3_stmt *pStmt; int rc = 0; int nKeep; db = cacheOpen(0); if( db==0 ) return; sqlite3_busy_timeout(db, 10000); sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0); pStmt = cacheStmt(db, "INSERT INTO blob(data) VALUES(?1)"); if( pStmt==0 ) goto cache_write_end; sqlite3_bind_blob(pStmt, 1, blob_buffer(pContent), blob_size(pContent), SQLITE_STATIC); if( sqlite3_step(pStmt)!=SQLITE_DONE ) goto cache_write_end; sqlite3_finalize(pStmt); pStmt = cacheStmt(db, "INSERT OR IGNORE INTO cache(key,sz,tm,nref,id)" "VALUES(?1,?2,strftime('%s','now'),1,?3)" ); if( pStmt==0 ) goto cache_write_end; sqlite3_bind_text(pStmt, 1, zKey, -1, SQLITE_STATIC); sqlite3_bind_int(pStmt, 2, blob_size(pContent)); sqlite3_bind_int(pStmt, 3, sqlite3_last_insert_rowid(db)); if( sqlite3_step(pStmt)!=SQLITE_DONE) goto cache_write_end; rc = sqlite3_changes(db); /* If the write was successful, truncate the cache to keep at most ** max-cache-entry entries in the cache */ if( rc ){ nKeep = db_get_int("max-cache-entry",10); sqlite3_finalize(pStmt); pStmt = cacheStmt(db, "DELETE FROM cache WHERE rowid IN (" "SELECT rowid FROM cache ORDER BY tm DESC" " LIMIT -1 OFFSET ?1)"); if( pStmt ){ sqlite3_bind_int(pStmt, 1, nKeep); sqlite3_step(pStmt); } } cache_write_end: sqlite3_finalize(pStmt); sqlite3_exec(db, rc ? "COMMIT" : "ROLLBACK", 0, 0, 0); sqlite3_close(db); } /* ** Attempt to read content out of the cache with the given zKey. Return ** non-zero on success and zero if unable to locate the content. ** ** Possible reasons for returning zero: ** (1) This server does not implement a cache ** (2) The requested element is not in the cache */ int cache_read(Blob *pContent, const char *zKey){ sqlite3 *db; sqlite3_stmt *pStmt; int rc = 0; db = cacheOpen(0); if( db==0 ) return 0; sqlite3_busy_timeout(db, 10000); sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0); pStmt = cacheStmt(db, "SELECT blob.data FROM cache, blob" " WHERE cache.key=?1 AND cache.id=blob.id"); if( pStmt==0 ) goto cache_read_done; sqlite3_bind_text(pStmt, 1, zKey, -1, SQLITE_STATIC); if( sqlite3_step(pStmt)==SQLITE_ROW ){ blob_append(pContent, sqlite3_column_blob(pStmt, 0), sqlite3_column_bytes(pStmt, 0)); rc = 1; sqlite3_reset(pStmt); pStmt = cacheStmt(db, "UPDATE cache SET nref=nref+1, tm=strftime('%s','now')" " WHERE key=?1"); if( pStmt ){ sqlite3_bind_text(pStmt, 1, zKey, -1, SQLITE_STATIC); sqlite3_step(pStmt); } } sqlite3_finalize(pStmt); cache_read_done: sqlite3_exec(db, "COMMIT", 0, 0, 0); sqlite3_close(db); return rc; } /* ** Create a cache database for the current repository if no such ** database already exists. */ void cache_initialize(void){ sqlite3_close(cacheOpen(1)); } /* ** COMMAND: cache* ** Usage: %fossil cache SUBCOMMAND ** ** Manage the cache used for potentially expensive web pages such as ** /zip and /tarball. SUBCOMMAND an be: ** ** clear Remove all entries from the cache. ** ** init Create the cache file if it does not already exists. ** ** list|ls List the keys and content sizes and other stats for ** all entries currently in the cache ** ** status Show a summary of cache status. ** ** The cache is stored in a file that is distinct from the repository ** but that is held in the same directory as the repository. To cache ** file can be deleted in order to completely disable the cache. */ void cache_cmd(void){ const char *zCmd; int nCmd; sqlite3 *db; sqlite3_stmt *pStmt; db_find_and_open_repository(0,0); zCmd = g.argc>=3 ? g.argv[2] : ""; nCmd = (int)strlen(zCmd); if( nCmd<=1 ){ fossil_fatal("Usage: %s cache SUBCOMMAND", g.argv[0]); } if( strncmp(zCmd, "init", nCmd)==0 ){ db = cacheOpen(0); sqlite3_close(db); if( db ){ fossil_print("cache already exists in file %z\n", cacheName()); }else{ db = cacheOpen(1); sqlite3_close(db); if( db ){ fossil_print("cache created in file %z\n", cacheName()); }else{ fossil_fatal("unable to create cache file %z", cacheName()); } } }else if( strncmp(zCmd, "clear", nCmd)==0 ){ db = cacheOpen(0); if( db ){ sqlite3_exec(db, "DELETE FROM cache; DELETE FROM blob; VACUUM;",0,0,0); sqlite3_close(db); fossil_print("cache cleared\n"); }else{ fossil_print("nothing to clear; cache does not exist\n"); } }else if(( strncmp(zCmd, "list", nCmd)==0 ) || ( strncmp(zCmd, "ls", nCmd)==0 )){ db = cacheOpen(0); if( db==0 ){ fossil_print("cache does not exist\n"); }else{ int nEntry = 0; char *zDbName = cacheName(); cache_register_sizename(db); pStmt = cacheStmt(db, "SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')" " FROM cache" " ORDER BY tm DESC" ); if( pStmt ){ while( sqlite3_step(pStmt)==SQLITE_ROW ){ fossil_print("%s %4d %8s %s\n", sqlite3_column_text(pStmt, 3), sqlite3_column_int(pStmt, 2), sqlite3_column_text(pStmt, 1), sqlite3_column_text(pStmt, 0)); nEntry++; } sqlite3_finalize(pStmt); } sqlite3_close(db); fossil_print("Entries: %d Cache-file Size: %lld\n", nEntry, file_size(zDbName)); fossil_free(zDbName); } }else if( strncmp(zCmd, "status", nCmd)==0 ){ fossil_print("TBD...\n"); }else{ fossil_fatal("Unknown subcommand \"%s\"." " Should be one of: clear init list status", zCmd); } } /* ** WEBPAGE: cachestat ** ** Show information about the webpage cache */ void cache_page(void){ sqlite3 *db; sqlite3_stmt *pStmt; char zBuf[100]; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_header("Web Cache Status"); db = cacheOpen(0); if( db==0 ){ @ The web-page cache is disabled for this repository }else{ char *zDbName = cacheName(); cache_register_sizename(db); pStmt = cacheStmt(db, "SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')" " FROM cache" " ORDER BY tm DESC" ); if( pStmt ){ @ <ol> while( sqlite3_step(pStmt)==SQLITE_ROW ){ const unsigned char *zName = sqlite3_column_text(pStmt,0); @ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br> @ size: %s(sqlite3_column_text(pStmt,1)) @ hit-count: %d(sqlite3_column_int(pStmt,2)) @ last-access: %s(sqlite3_column_text(pStmt,3))</p></li> } sqlite3_finalize(pStmt); @ </ol> } zDbName = cacheName(); bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName)); @ <p>cache-file name: %h(zDbName)</p> @ <p>cache-file size: %s(zBuf)</p> fossil_free(zDbName); sqlite3_close(db); } style_footer(); } /* ** WEBPAGE: cacheget ** ** Usage: /cacheget?key=KEY ** ** Download a single entry for the cache, identified by KEY. ** This page is normally a hyperlink from the /cachestat page. */ void cache_getpage(void){ const char *zKey; Blob content; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } zKey = PD("key",""); blob_zero(&content); if( cache_read(&content, zKey)==0 ){ style_header("Cache Download Error"); @ The cache does not contain any entry with this key: "%h(zKey)" style_footer(); return; } cgi_set_content(&content); cgi_set_content_type("application/x-compressed"); } |
Changes to src/captcha.c.
︙ | ︙ | |||
15 16 17 18 19 20 21 | ** ******************************************************************************* ** ** This file contains code to a simple text-based CAPTCHA. Though easily ** defeated by a sophisticated attacker, this CAPTCHA does at least make ** scripting attacks more difficult. */ | < > | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ** ******************************************************************************* ** ** This file contains code to a simple text-based CAPTCHA. Though easily ** defeated by a sophisticated attacker, this CAPTCHA does at least make ** scripting attacks more difficult. */ #include "config.h" #include <assert.h> #include "captcha.h" #if INTERFACE #define CAPTCHA 3 /* Which captcha rendering to use */ #endif /* ** Convert a hex digit into a value between 0 and 15 */ int hex_digit_value(char c){ if( c>='0' && c<='9' ){ return c - '0'; }else if( c>='a' && c<='f' ){ return c - 'a' + 10; }else if( c>='A' && c<='F' ){ return c - 'A' + 10; }else{ |
︙ | ︙ | |||
73 74 75 76 77 78 79 | char *captcha_render(const char *zPw){ char *z = fossil_malloc( 9*6*strlen(zPw) + 7 ); int i, j, k, m; k = 0; for(i=0; i<6; i++){ for(j=0; zPw[j]; j++){ | | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | char *captcha_render(const char *zPw){ char *z = fossil_malloc( 9*6*strlen(zPw) + 7 ); int i, j, k, m; k = 0; for(i=0; i<6; i++){ for(j=0; zPw[j]; j++){ unsigned char v = hex_digit_value(zPw[j]); v = (aFont1[v] >> ((5-i)*4)) & 0xf; for(m=8; m>=1; m = m>>1){ if( v & m ){ z[k++] = 'X'; z[k++] = 'X'; }else{ z[k++] = ' '; z[k++] = ' '; } } z[k++] = ' '; z[k++] = ' '; } z[k++] = '\n'; } z[k] = 0; return z; } #endif /* CAPTCHA==1 */ #if CAPTCHA==2 static const char *const azFont2[] = { /* 0 */ |
︙ | ︙ | |||
146 147 148 149 150 151 152 | /* 7 */ " ____ ", "|__ |", " / / ", " /_/ ", /* 8 */ | | | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | /* 7 */ " ____ ", "|__ |", " / / ", " /_/ ", /* 8 */ " ___ ", "( _ )", "/ _ \\", "\\___/", /* 9 */ " ___ ", "/ _ \\", |
︙ | ︙ | |||
207 208 209 210 211 212 213 | char *z = fossil_malloc( 7*4*strlen(zPw) + 5 ); int i, j, k, m; const char *zChar; k = 0; for(i=0; i<4; i++){ for(j=0; zPw[j]; j++){ | | | | | | | | | | | | | | | | | | | 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | char *z = fossil_malloc( 7*4*strlen(zPw) + 5 ); int i, j, k, m; const char *zChar; k = 0; for(i=0; i<4; i++){ for(j=0; zPw[j]; j++){ unsigned char v = hex_digit_value(zPw[j]); zChar = azFont2[4*v + i]; for(m=0; zChar[m]; m++){ z[k++] = zChar[m]; } } z[k++] = '\n'; } z[k] = 0; return z; } #endif /* CAPTCHA==2 */ #if CAPTCHA==3 static const char *const azFont3[] = { /* 0 */ " ___ ", " / _ \\ ", "| | | |", "| | | |", "| |_| |", " \\___/ ", /* 1 */ " __ ", "/_ |", " | |", " | |", " | |", " |_|", /* 2 */ " ___ ", "|__ \\ ", " ) |", " / / ", " / /_ ", "|____|", /* 3 */ " ____ ", "|___ \\ ", " __) |", " |__ < ", " ___) |", "|____/ ", /* 4 */ " _ _ ", "| || | ", "| || |_ ", "|__ _|", " | | ", " |_| ", /* 5 */ " _____ ", "| ____|", "| |__ ", "|___ \\ ", " ___) |", "|____/ ", /* 6 */ " __ ", " / / ", " / /_ ", "| '_ \\ ", "| (_) |", " \\___/ ", /* 7 */ " ______ ", "|____ |", " / / ", " / / ", " / / ", " /_/ ", /* 8 */ " ___ ", " / _ \\ ", "| (_) |", " > _ < ", "| (_) |", " \\___/ ", /* 9 */ " ___ ", " / _ \\ ", "| (_) |", " \\__, |", " / / ", " /_/ ", /* A */ " ", " /\\ ", " / \\ ", " / /\\ \\ ", " / ____ \\ ", "/_/ \\_\\", /* B */ " ____ ", "| _ \\ ", "| |_) |", "| _ < ", "| |_) |", "|____/ ", /* C */ " _____ ", " / ____|", "| | ", "| | ", "| |____ ", " \\_____|", /* D */ " _____ ", "| __ \\ ", "| | | |", "| | | |", "| |__| |", "|_____/ ", /* E */ " ______ ", "| ____|", "| |__ ", "| __| ", "| |____ ", "|______|", /* F */ " ______ ", "| ____|", "| |__ ", "| __| ", "| | ", "|_| ", |
︙ | ︙ | |||
367 368 369 370 371 372 373 | unsigned char x; int y; k = 0; for(i=0; i<6; i++){ x = 0; for(j=0; zPw[j]; j++){ | | | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | unsigned char x; int y; k = 0; for(i=0; i<6; i++){ x = 0; for(j=0; zPw[j]; j++){ unsigned char v = hex_digit_value(zPw[j]); x = (x<<4) + v; switch( x ){ case 0x7a: case 0xfa: y = 3; break; case 0x47: |
︙ | ︙ | |||
406 407 408 409 410 411 412 | for(m=0; zChar[m]; m++){ z[k++] = zChar[m]; } } z[k++] = '\n'; } z[k] = 0; | | | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | for(m=0; zChar[m]; m++){ z[k++] = zChar[m]; } } z[k++] = '\n'; } z[k] = 0; return z; } #endif /* CAPTCHA==3 */ /* ** COMMAND: test-captcha */ void test_captcha(void){ |
︙ | ︙ | |||
446 447 448 449 450 451 452 | } /* ** Translate a captcha seed value into the captcha password string. ** The returned string is static and overwritten on each call to ** this function. */ | | | 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 | } /* ** Translate a captcha seed value into the captcha password string. ** The returned string is static and overwritten on each call to ** this function. */ const char *captcha_decode(unsigned int seed){ const char *zSecret; const char *z; Blob b; static char zRes[20]; zSecret = db_get("captcha-secret", 0); if( zSecret==0 ){ |
︙ | ︙ | |||
476 477 478 479 480 481 482 | /* ** Return true if a CAPTCHA is required for editing wiki or tickets or for ** adding attachments. ** ** A CAPTCHA is required in those cases if the user is not logged in (if they ** are user "nobody") and if the "require-captcha" setting is true. The | | < | | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 | /* ** Return true if a CAPTCHA is required for editing wiki or tickets or for ** adding attachments. ** ** A CAPTCHA is required in those cases if the user is not logged in (if they ** are user "nobody") and if the "require-captcha" setting is true. The ** "require-captcha" setting is controlled on the Admin/Access page. It ** defaults to true. */ int captcha_needed(void){ return login_is_nobody() && db_get_boolean("require-captcha", 1); } /* ** If a captcha is required but the correct captcha code is not supplied ** in the query parameters, then return false (0). ** ** If no captcha is required or if the correct captcha is supplied, return |
︙ | ︙ | |||
516 517 518 519 520 521 522 | if( strlen(zEntered)!=8 ) return 0; for(i=0; i<8; i++){ char c = zEntered[i]; if( c>='A' && c<='F' ) c += 'a' - 'A'; if( c=='O' ) c = '0'; z[i] = c; } | | | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 | if( strlen(zEntered)!=8 ) return 0; for(i=0; i<8; i++){ char c = zEntered[i]; if( c>='A' && c<='F' ) c += 'a' - 'A'; if( c=='O' ) c = '0'; z[i] = c; } if( strncmp(zDecode,z,8)!=0 ) return 0; return 1; } /* ** Generate a captcha display together with the necessary hidden parameter ** for the seed and the entry box into which the user will type the text of ** the captcha. This is typically done at the very bottom of a form. |
︙ | ︙ |
Changes to src/cgi.c.
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | ** or cookie "x", or NULL if there is no such parameter or cookie. PD("x","y") ** does the same except "y" is returned in place of NULL if there is not match. */ #define P(x) cgi_parameter((x),0) #define PD(x,y) cgi_parameter((x),(y)) #define PT(x) cgi_parameter_trimmed((x),0) #define PDT(x,y) cgi_parameter_trimmed((x),(y)) /* ** Destinations for output text. */ #define CGI_HEADER 0 #define CGI_BODY 1 #endif /* INTERFACE */ /* ** The HTTP reply is generated in two pieces: the header and the body. ** These pieces are generated separately because they are not necessary ** produced in order. Parts of the header might be built after all or ** part of the body. The header and body are accumulated in separate | > > > > > > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | ** or cookie "x", or NULL if there is no such parameter or cookie. PD("x","y") ** does the same except "y" is returned in place of NULL if there is not match. */ #define P(x) cgi_parameter((x),0) #define PD(x,y) cgi_parameter((x),(y)) #define PT(x) cgi_parameter_trimmed((x),0) #define PDT(x,y) cgi_parameter_trimmed((x),(y)) #define PB(x) cgi_parameter_boolean(x) /* ** Destinations for output text. */ #define CGI_HEADER 0 #define CGI_BODY 1 /* ** Flags for SSH HTTP clients */ #define CGI_SSH_CLIENT 0x0001 /* Client is SSH */ #define CGI_SSH_COMPAT 0x0002 /* Compat for old SSH transport */ #define CGI_SSH_FOSSIL 0x0004 /* Use new Fossil SSH transport */ #endif /* INTERFACE */ /* ** The HTTP reply is generated in two pieces: the header and the body. ** These pieces are generated separately because they are not necessary ** produced in order. Parts of the header might be built after all or ** part of the body. The header and body are accumulated in separate |
︙ | ︙ | |||
99 100 101 102 103 104 105 106 107 108 109 110 111 112 | /* ** Check to see if the header contains the zNeedle string. Return true ** if it does and false if it does not. */ int cgi_header_contains(const char *zNeedle){ return strstr(blob_str(&cgiContent[0]), zNeedle)!=0; } /* ** Append reply content to what already exists. */ void cgi_append_content(const char *zData, int nAmt){ blob_append(pContent, zData, nAmt); } | > > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | /* ** Check to see if the header contains the zNeedle string. Return true ** if it does and false if it does not. */ int cgi_header_contains(const char *zNeedle){ return strstr(blob_str(&cgiContent[0]), zNeedle)!=0; } int cgi_body_contains(const char *zNeedle){ return strstr(blob_str(&cgiContent[1]), zNeedle)!=0; } /* ** Append reply content to what already exists. */ void cgi_append_content(const char *zData, int nAmt){ blob_append(pContent, zData, nAmt); } |
︙ | ︙ | |||
257 258 259 260 261 262 263 | for( zTok = strtok_r(zBuf, ",\"",&zPos); zTok && fossil_stricmp(zTok,zETag); zTok = strtok_r(0, ",\"",&zPos)){} fossil_free(zBuf); if(zTok) return 1; } } | | > > > > > > > > > > | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | for( zTok = strtok_r(zBuf, ",\"",&zPos); zTok && fossil_stricmp(zTok,zETag); zTok = strtok_r(0, ",\"",&zPos)){} fossil_free(zBuf); if(zTok) return 1; } } return 0; } #endif /* ** Return true if the response should be sent with Content-Encoding: gzip. */ static int is_gzippable(void){ if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0; return strncmp(zContentType, "text/", 5)==0 || sqlite3_strglob("application/*xml", zContentType)==0 || sqlite3_strglob("application/*javascript", zContentType)==0; } /* ** Do a normal HTTP reply */ void cgi_reply(void){ int total_size; if( iReplyStatus<=0 ){ |
︙ | ︙ | |||
287 288 289 290 291 292 293 294 295 296 297 298 299 300 | } #endif if( g.fullHttpReply ){ fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); fprintf(g.httpOut, "Connection: close\r\n"); }else{ fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); } if( blob_size(&extraHeader)>0 ){ fprintf(g.httpOut, "%s", blob_buffer(&extraHeader)); } | > | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | } #endif if( g.fullHttpReply ){ fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); fprintf(g.httpOut, "Connection: close\r\n"); fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); }else{ fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); } if( blob_size(&extraHeader)>0 ){ fprintf(g.httpOut, "%s", blob_buffer(&extraHeader)); } |
︙ | ︙ | |||
322 323 324 325 326 327 328 | ** else. In the case of attachments, the contents won't change because ** an attempt to change them generates a new attachment number. In the ** case of most /getfile calls for specific versions, the only way the ** content changes is if someone breaks the SCM. And if that happens, a ** stale cache is the least of the problem. So we provide an Expires ** header set to a reasonable period (default: one week). */ | < < | > > > > > > > > > > > > | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | ** else. In the case of attachments, the contents won't change because ** an attempt to change them generates a new attachment number. In the ** case of most /getfile calls for specific versions, the only way the ** content changes is if someone breaks the SCM. And if that happens, a ** stale cache is the least of the problem. So we provide an Expires ** header set to a reasonable period (default: one week). */ fprintf(g.httpOut, "Cache-control: max-age=28800\r\n"); }else{ fprintf(g.httpOut, "Cache-control: no-cache\r\n"); } /* Content intended for logged in users should only be cached in ** the browser, not some shared location. */ fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType); if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ cgi_combine_header_and_body(); blob_compress(&cgiContent[0], &cgiContent[0]); } if( iReplyStatus != 304 ) { if( is_gzippable() ){ int i; gzip_begin(0); for( i=0; i<2; i++ ){ int size = blob_size(&cgiContent[i]); if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size); blob_reset(&cgiContent[i]); } gzip_finish(&cgiContent[0]); fprintf(g.httpOut, "Content-Encoding: gzip\r\n"); fprintf(g.httpOut, "Vary: Accept-Encoding\r\n"); } total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]); fprintf(g.httpOut, "Content-Length: %d\r\n", total_size); }else{ total_size = 0; } fprintf(g.httpOut, "\r\n"); if( total_size>0 && iReplyStatus != 304 ){ |
︙ | ︙ | |||
403 404 405 406 407 408 409 | static int nUsedQP = 0; /* Space actually used in aParamQP[] */ static int sortQP = 0; /* True if aParamQP[] needs sorting */ static int seqQP = 0; /* Sequence numbers */ static struct QParam { /* One entry for each query parameter or cookie */ const char *zName; /* Parameter or cookie name */ const char *zValue; /* Value of the query parameter or cookie */ int seq; /* Order of insertion */ | | > | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 | static int nUsedQP = 0; /* Space actually used in aParamQP[] */ static int sortQP = 0; /* True if aParamQP[] needs sorting */ static int seqQP = 0; /* Sequence numbers */ static struct QParam { /* One entry for each query parameter or cookie */ const char *zName; /* Parameter or cookie name */ const char *zValue; /* Value of the query parameter or cookie */ int seq; /* Order of insertion */ char isQP; /* True for query parameters */ char cTag; /* Tag on query parameters */ } *aParamQP; /* An array of all parameters and cookies */ /* ** Add another query parameter or cookie to the parameter set. ** zName is the name of the query parameter or cookie and zValue ** is its fully decoded value. ** |
︙ | ︙ | |||
430 431 432 433 434 435 436 437 438 439 440 441 442 443 | aParamQP[nUsedQP].zName = zName; aParamQP[nUsedQP].zValue = zValue; if( g.fHttpTrace ){ fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue); } aParamQP[nUsedQP].seq = seqQP++; aParamQP[nUsedQP].isQP = isQP; nUsedQP++; sortQP = 1; } /* ** Add another query parameter or cookie to the parameter set. ** zName is the name of the query parameter or cookie and zValue | > | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 | aParamQP[nUsedQP].zName = zName; aParamQP[nUsedQP].zValue = zValue; if( g.fHttpTrace ){ fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue); } aParamQP[nUsedQP].seq = seqQP++; aParamQP[nUsedQP].isQP = isQP; aParamQP[nUsedQP].cTag = 0; nUsedQP++; sortQP = 1; } /* ** Add another query parameter or cookie to the parameter set. ** zName is the name of the query parameter or cookie and zValue |
︙ | ︙ | |||
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){ aParamQP[i].zValue = zValue; return; } } cgi_set_parameter_nocopy(zName, zValue, 0); } /* ** Add a query parameter. The zName portion is fixed but a copy ** must be made of zValue. */ void cgi_setenv(const char *zName, const char *zValue){ cgi_set_parameter_nocopy(zName, mprintf("%s",zValue), 0); } | > > > > > > > > > > > | | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){ aParamQP[i].zValue = zValue; return; } } cgi_set_parameter_nocopy(zName, zValue, 0); } void cgi_replace_query_parameter(const char *zName, const char *zValue){ int i; for(i=0; i<nUsedQP; i++){ if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){ aParamQP[i].zValue = zValue; assert( aParamQP[i].isQP ); return; } } cgi_set_parameter_nocopy(zName, zValue, 1); } /* ** Add a query parameter. The zName portion is fixed but a copy ** must be made of zValue. */ void cgi_setenv(const char *zName, const char *zValue){ cgi_set_parameter_nocopy(zName, mprintf("%s",zValue), 0); } /* ** Add a list of query parameters or cookies to the parameter set. ** ** Each parameter is of the form NAME=VALUE. Both the NAME and the ** VALUE may be url-encoded ("+" for space, "%HH" for other special ** characters). But this routine assumes that NAME contains no |
︙ | ︙ | |||
584 585 586 587 588 589 590 | *pnContent = i; i += nBoundry; break; } } *pz = &z[i]; get_line_from_string(pz, pLen); | | | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 | *pnContent = i; i += nBoundry; break; } } *pz = &z[i]; get_line_from_string(pz, pLen); return z; } /* ** Tokenize a line of text into as many as nArg tokens. Make ** azArg[] point to the start of each token. ** ** Tokens consist of space or semi-colon delimited words or |
︙ | ︙ | |||
691 692 693 694 695 696 697 | char *z = azArg[++i]; if( zName && z && fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z, 1); } } } } | | | 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 | char *z = azArg[++i]; if( zName && z && fossil_islower(zName[0]) ){ cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z, 1); } } } } } } #ifdef FOSSIL_ENABLE_JSON /* ** Internal helper for cson_data_source_FILE_n(). */ |
︙ | ︙ | |||
719 720 721 722 723 724 725 | unsigned int * n ){ if( ! state || !dest || !n ) return cson_rc.ArgError; else { CgiPostReadState * st = (CgiPostReadState *)state; if( st->pos >= st->len ){ *n = 0; return 0; | | | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 | unsigned int * n ){ if( ! state || !dest || !n ) return cson_rc.ArgError; else { CgiPostReadState * st = (CgiPostReadState *)state; if( st->pos >= st->len ){ *n = 0; return 0; }else if( !*n || ((st->pos + *n) > st->len) ){ return cson_rc.RangeError; }else{ unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh ); if( ! rsz ){ *n = rsz; return feof(st->fh) ? 0 : cson_rc.IOError; }else{ |
︙ | ︙ | |||
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 | /* Forward declaration */ static NORETURN void malformed_request(const char *zMsg); /* ** Initialize the query parameter database. Information is pulled from ** the QUERY_STRING environment variable (if it exists), from standard ** input if there is POST data, and from HTTP_COOKIE. */ void cgi_init(void){ char *z; const char *zType; int len; const char *zRequestUri = cgi_parameter("REQUEST_URI",0); const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); #ifdef FOSSIL_ENABLE_JSON json_main_bootstrap(); #endif g.isHTTP = 1; cgi_destination(CGI_BODY); | > > > > > > > > > > > > > > > > > | > > | > > > | > > | | | 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 | /* Forward declaration */ static NORETURN void malformed_request(const char *zMsg); /* ** Initialize the query parameter database. Information is pulled from ** the QUERY_STRING environment variable (if it exists), from standard ** input if there is POST data, and from HTTP_COOKIE. ** ** REQUEST_URI, PATH_INFO, and SCRIPT_NAME are related as follows: ** ** REQUEST_URI == SCRIPT_NAME + PATH_INFO ** ** Where "+" means concatenate. Fossil requires SCRIPT_NAME. If ** REQUEST_URI is provided but PATH_INFO is not, then PATH_INFO is ** computed from REQUEST_URI and SCRIPT_NAME. If PATH_INFO is provided ** but REQUEST_URI is not, then compute REQUEST_URI from PATH_INFO and ** SCRIPT_NAME. If neither REQUEST_URI nor PATH_INFO are provided, then ** assume that PATH_INFO is an empty string and set REQUEST_URI equal ** to PATH_INFO. ** ** SCGI typically omits PATH_INFO. CGI sometimes omits REQUEST_URI and ** PATH_INFO when it is empty. */ void cgi_init(void){ char *z; const char *zType; int len; const char *zRequestUri = cgi_parameter("REQUEST_URI",0); const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); const char *zPathInfo = cgi_parameter("PATH_INFO",0); #ifdef FOSSIL_ENABLE_JSON json_main_bootstrap(); #endif g.isHTTP = 1; cgi_destination(CGI_BODY); if( zScriptName==0 ) malformed_request("missing SCRIPT_NAME"); if( zRequestUri==0 ){ const char *z = zPathInfo; if( zPathInfo==0 ){ malformed_request("missing PATH_INFO and/or REQUEST_URI"); } if( z[0]=='/' ) z++; zRequestUri = mprintf("%s/%s", zScriptName, z); cgi_set_parameter("REQUEST_URI", zRequestUri); } if( zPathInfo==0 ){ int i, j; for(i=0; zRequestUri[i]==zScriptName[i] && zRequestUri[i]; i++){} for(j=i; zRequestUri[j] && zRequestUri[j]!='?'; j++){} cgi_set_parameter("PATH_INFO", mprintf("%.*s", j-i, zRequestUri+i)); } z = (char*)P("HTTP_COOKIE"); if( z ){ z = mprintf("%s",z); add_param_list(z, ';'); } z = (char*)P("QUERY_STRING"); if( z ){ z = mprintf("%s",z); add_param_list(z, '&'); } z = (char*)P("REMOTE_ADDR"); if( z ){ g.zIpAddr = mprintf("%s", z); } len = atoi(PD("CONTENT_LENGTH", "0")); g.zContentType = zType = P("CONTENT_TYPE"); blob_zero(&g.cgiIn); if( len>0 && zType ){ if( fossil_strcmp(zType,"application/x-www-form-urlencoded")==0 || strncmp(zType,"multipart/form-data",19)==0 ){ z = fossil_malloc( len+1 ); len = fread(z, 1, len, g.httpIn); z[len] = 0; cgi_trace(z); if( zType[0]=='a' ){ add_param_list(z, '&'); |
︙ | ︙ | |||
894 895 896 897 898 899 900 | g.json.isJsonMode = 1; cgi_parse_POST_JSON(g.httpIn, (unsigned int)len); /* FIXMEs: - See if fossil really needs g.cgiIn to be set for this purpose (i don't think it does). If it does then fill g.cgiIn and refactor to parse the JSON from there. | | | 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 | g.json.isJsonMode = 1; cgi_parse_POST_JSON(g.httpIn, (unsigned int)len); /* FIXMEs: - See if fossil really needs g.cgiIn to be set for this purpose (i don't think it does). If it does then fill g.cgiIn and refactor to parse the JSON from there. - After parsing POST JSON, copy the "first layer" of keys/values to cgi_setenv(), honoring the upper-case distinction used in add_param_list(). However... - If we do that then we might get a disconnect in precedence of GET/POST arguments. i prefer for GET entries to take precedence over like-named POST entries, but in order for that to happen we |
︙ | ︙ | |||
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 | if( zIn==0 ) zIn = zDefault; while( fossil_isspace(zIn[0]) ) zIn++; zOut = fossil_strdup(zIn); for(i=0; zOut[i]; i++){} while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0; return zOut; } /* ** Return the name of the i-th CGI parameter. Return NULL if there ** are fewer than i registered CGI parameters. */ const char *cgi_parameter_name(int i){ if( i>=0 && i<nUsedQP ){ | > > > > > > > > > > | 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 | if( zIn==0 ) zIn = zDefault; while( fossil_isspace(zIn[0]) ) zIn++; zOut = fossil_strdup(zIn); for(i=0; zOut[i]; i++){} while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0; return zOut; } /* ** Return true if the CGI parameter zName exists and is not equal to 0, ** or "no" or "off". */ int cgi_parameter_boolean(const char *zName){ const char *zIn = cgi_parameter(zName, 0); if( zIn==0 ) return 0; return zIn[0]==0 || is_truth(zIn); } /* ** Return the name of the i-th CGI parameter. Return NULL if there ** are fewer than i registered CGI parameters. */ const char *cgi_parameter_name(int i){ if( i>=0 && i<nUsedQP ){ |
︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 | if( fossil_strnicmp("fossil-",zName,7)==0 ) continue; } cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue); } } /* | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 | if( fossil_strnicmp("fossil-",zName,7)==0 ) continue; } cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue); } } /* ** Export all untagged query parameters (but not cookies or environment ** variables) as hidden values of a form. */ void cgi_query_parameters_to_hidden(void){ int i; const char *zN, *zV; for(i=0; i<nUsedQP; i++){ if( aParamQP[i].isQP==0 || aParamQP[i].cTag ) continue; zN = aParamQP[i].zName; zV = aParamQP[i].zValue; @ <input type="hidden" name="%h(zN)" value="%h(zV)"> } } /* ** Export all untagged query parameters (but not cookies or environment ** variables) to the HQuery object. */ void cgi_query_parameters_to_url(HQuery *p){ int i; for(i=0; i<nUsedQP; i++){ if( aParamQP[i].isQP==0 || aParamQP[i].cTag ) continue; url_add_parameter(p, aParamQP[i].zName, aParamQP[i].zValue); } } /* ** Tag query parameter zName so that it is not exported by ** cgi_query_parameters_to_hidden(). Or if zName==0, then ** untag all query parameters. */ void cgi_tag_query_parameter(const char *zName){ int i; if( zName==0 ){ for(i=0; i<nUsedQP; i++) aParamQP[i].cTag = 0; }else{ for(i=0; i<nUsedQP; i++){ if( strcmp(zName,aParamQP[i].zName)==0 ) aParamQP[i].cTag = 1; } } } /* ** This routine works like "printf" except that it has the ** extra formatting capabilities such as %h and %t. */ void cgi_printf(const char *zFormat, ...){ va_list ap; |
︙ | ︙ | |||
1170 1171 1172 1173 1174 1175 1176 | ** Return a pointer to a string containing the real IP address, or a ** NULL pointer to stick with the IP address previously computed and ** loaded into g.zIpAddr. */ static const char *cgi_accept_forwarded_for(const char *z){ int i; if( fossil_strcmp(g.zIpAddr, "127.0.0.1")!=0 ) return 0; | | | 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 | ** Return a pointer to a string containing the real IP address, or a ** NULL pointer to stick with the IP address previously computed and ** loaded into g.zIpAddr. */ static const char *cgi_accept_forwarded_for(const char *z){ int i; if( fossil_strcmp(g.zIpAddr, "127.0.0.1")!=0 ) return 0; i = strlen(z)-1; while( i>=0 && z[i]!=',' && !fossil_isspace(z[i]) ) i--; return &z[++i]; } /* ** Remove the first space-delimited token from a string and return |
︙ | ︙ | |||
1242 1243 1244 1245 1246 1247 1248 | cgi_setenv("REQUEST_URI", zToken); cgi_setenv("SCRIPT_NAME", ""); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); if( zIpAddr==0 && | | | | > > | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | | 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 | cgi_setenv("REQUEST_URI", zToken); cgi_setenv("SCRIPT_NAME", ""); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); if( zIpAddr==0 && getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, &size)>=0 ){ zIpAddr = inet_ntoa(remoteName.sin_addr); } if( zIpAddr ){ cgi_setenv("REMOTE_ADDR", zIpAddr); g.zIpAddr = mprintf("%s", zIpAddr); } /* Get all the optional fields that follow the first line. */ while( fgets(zLine,sizeof(zLine),g.httpIn) ){ char *zFieldName; char *zVal; cgi_trace(zLine); blob_append(&g.httpHeader, zLine, -1); zFieldName = extract_token(zLine,&zVal); if( zFieldName==0 || *zFieldName==0 ) break; while( fossil_isspace(*zVal) ){ zVal++; } i = strlen(zVal); while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; } zVal[i] = 0; for(i=0; zFieldName[i]; i++){ zFieldName[i] = fossil_tolower(zFieldName[i]); } if( fossil_strcmp(zFieldName,"accept-encoding:")==0 ){ cgi_setenv("HTTP_ACCEPT_ENCODING", zVal); }else if( fossil_strcmp(zFieldName,"content-length:")==0 ){ cgi_setenv("CONTENT_LENGTH", zVal); }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){ cgi_setenv("CONTENT_TYPE", zVal); }else if( fossil_strcmp(zFieldName,"cookie:")==0 ){ cgi_setenv("HTTP_COOKIE", zVal); }else if( fossil_strcmp(zFieldName,"https:")==0 ){ cgi_setenv("HTTPS", zVal); }else if( fossil_strcmp(zFieldName,"host:")==0 ){ cgi_setenv("HTTP_HOST", zVal); }else if( fossil_strcmp(zFieldName,"if-none-match:")==0 ){ cgi_setenv("HTTP_IF_NONE_MATCH", zVal); }else if( fossil_strcmp(zFieldName,"if-modified-since:")==0 ){ cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal); }else if( fossil_strcmp(zFieldName,"referer:")==0 ){ cgi_setenv("HTTP_REFERER", zVal); }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){ cgi_setenv("HTTP_USER_AGENT", zVal); }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ const char *zIpAddr = cgi_accept_forwarded_for(zVal); if( zIpAddr!=0 ){ g.zIpAddr = mprintf("%s", zIpAddr); cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr); } } } cgi_init(); cgi_trace(0); } /* ** This routine handles a single HTTP request from an SSH client which is ** coming in on g.httpIn and which replies on g.httpOut ** ** Once all the setup is finished, this procedure returns ** and subsequent code handles the actual generation of the webpage. ** ** It is called in a loop so some variables will need to be replaced */ void cgi_handle_ssh_http_request(const char *zIpAddr){ static int nCycles = 0; static char *zCmd = 0; char *z, *zToken; const char *zType = 0; int i, content_length = 0; char zLine[2000]; /* A single line of input. */ if( zIpAddr ){ if( nCycles==0 ){ cgi_setenv("REMOTE_ADDR", zIpAddr); g.zIpAddr = mprintf("%s", zIpAddr); } }else{ fossil_panic("missing SSH IP address"); } if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ malformed_request("missing HTTP header"); } cgi_trace(zLine); zToken = extract_token(zLine, &z); if( zToken==0 ){ malformed_request("malformed HTTP header"); } if( fossil_strcmp(zToken, "echo")==0 ){ /* start looking for probes to complete transport_open */ zCmd = cgi_handle_ssh_probes(zLine, sizeof(zLine), z, zToken); if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ malformed_request("missing HTTP header"); } cgi_trace(zLine); zToken = extract_token(zLine, &z); if( zToken==0 ){ malformed_request("malformed HTTP header"); } }else if( zToken && strlen(zToken)==0 && zCmd ){ /* transport_flip request and continued transport_open */ cgi_handle_ssh_transport(zCmd); if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ malformed_request("missing HTTP header"); } cgi_trace(zLine); zToken = extract_token(zLine, &z); if( zToken==0 ){ malformed_request("malformed HTTP header"); } } if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 && fossil_strcmp(zToken,"HEAD")!=0 ){ malformed_request("unsupported HTTP method"); } if( nCycles==0 ){ cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); cgi_setenv("REQUEST_METHOD",zToken); } zToken = extract_token(z, &z); if( zToken==0 ){ malformed_request("malformed URL in HTTP header"); } if( nCycles==0 ){ cgi_setenv("REQUEST_URI", zToken); cgi_setenv("SCRIPT_NAME", ""); } for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; if( nCycles==0 ){ cgi_setenv("PATH_INFO", zToken); }else{ cgi_replace_parameter("PATH_INFO", mprintf("%s",zToken)); } /* Get all the optional fields that follow the first line. */ while( fgets(zLine,sizeof(zLine),g.httpIn) ){ char *zFieldName; char *zVal; cgi_trace(zLine); zFieldName = extract_token(zLine,&zVal); if( zFieldName==0 || *zFieldName==0 ) break; while( fossil_isspace(*zVal) ){ zVal++; } i = strlen(zVal); while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; } zVal[i] = 0; for(i=0; zFieldName[i]; i++){ zFieldName[i] = fossil_tolower(zFieldName[i]); } if( fossil_strcmp(zFieldName,"content-length:")==0 ){ content_length = atoi(zVal); }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){ g.zContentType = zType = mprintf("%s", zVal); }else if( fossil_strcmp(zFieldName,"host:")==0 ){ if( nCycles==0 ){ cgi_setenv("HTTP_HOST", zVal); } }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){ if( nCycles==0 ){ cgi_setenv("HTTP_USER_AGENT", zVal); } }else if( fossil_strcmp(zFieldName,"x-fossil-transport:")==0 ){ if( fossil_strnicmp(zVal, "ssh", 3)==0 ){ if( nCycles==0 ){ g.fSshClient |= CGI_SSH_FOSSIL; g.fullHttpReply = 0; } } } } if( nCycles==0 ){ if( ! ( g.fSshClient & CGI_SSH_FOSSIL ) ){ /* did not find new fossil ssh transport */ g.fSshClient &= ~CGI_SSH_CLIENT; g.fullHttpReply = 1; cgi_replace_parameter("REMOTE_ADDR", "127.0.0.1"); } } cgi_reset_content(); cgi_destination(CGI_BODY); if( content_length>0 && zType ){ blob_zero(&g.cgiIn); if( fossil_strcmp(zType, "application/x-fossil")==0 ){ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length); blob_uncompress(&g.cgiIn, &g.cgiIn); }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length); }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length); } } cgi_trace(0); nCycles++; } /* ** This routine handles the old fossil SSH probes */ char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){ /* Start looking for probes */ while( fossil_strcmp(zToken, "echo")==0 ){ zToken = extract_token(z, &z); if( zToken==0 ){ malformed_request("malformed probe"); } if( fossil_strncmp(zToken, "test", 4)==0 || fossil_strncmp(zToken, "probe-", 6)==0 ){ fprintf(g.httpOut, "%s\n", zToken); fflush(g.httpOut); }else{ malformed_request("malformed probe"); } if( fgets(zLine, zSize, g.httpIn)==0 ){ malformed_request("malformed probe"); } cgi_trace(zLine); zToken = extract_token(zLine, &z); if( zToken==0 ){ malformed_request("malformed probe"); } } /* Got all probes now first transport_open is completed ** so return the command that was requested */ g.fSshClient |= CGI_SSH_COMPAT; return mprintf("%s", zToken); } /* ** This routine handles the old fossil SSH transport_flip ** and transport_open communications if detected. */ void cgi_handle_ssh_transport(const char *zCmd){ char *z, *zToken; char zLine[2000]; /* A single line of input. */ /* look for second newline of transport_flip */ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ malformed_request("incorrect transport_flip"); } cgi_trace(zLine); zToken = extract_token(zLine, &z); if( zToken && strlen(zToken)==0 ){ /* look for path to fossil */ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ if( zCmd==0 ){ malformed_request("missing fossil command"); }else{ /* no new command so exit */ fossil_exit(0); } } cgi_trace(zLine); zToken = extract_token(zLine, &z); if( zToken==0 ){ malformed_request("malformed fossil command"); } /* see if we've seen the command */ if( zCmd && zCmd[0] && fossil_strcmp(zToken, zCmd)==0 ){ return; }else{ malformed_request("transport_open failed"); } }else{ malformed_request("transport_flip failed"); } } /* ** This routine handles a single SCGI request which is coming in on ** g.httpIn and which replies on g.httpOut ** ** The SCGI request is read from g.httpIn and is used to initialize ** entries in the cgi_parameter() hash, as if those entries were ** environment variables. A call to cgi_init() completes ** the setup. Once all the setup is finished, this procedure returns ** and subsequent code handles the actual generation of the webpage. */ void cgi_handle_scgi_request(void){ char *zHdr; char *zToFree; int nHdr = 0; int nRead; int c, n, m; while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit((char)c) ){ nHdr = nHdr*10 + (char)c - '0'; } if( nHdr<16 ) malformed_request("SCGI header too short"); zToFree = zHdr = fossil_malloc(nHdr); nRead = (int)fread(zHdr, 1, nHdr, g.httpIn); if( nRead<nHdr ) malformed_request("cannot read entire SCGI header"); nHdr = nRead; while( nHdr ){ |
︙ | ︙ | |||
1342 1343 1344 1345 1346 1347 1348 | fossil_free(zToFree); fgetc(g.httpIn); /* Read past the "," separating header from content */ cgi_init(); } #if INTERFACE | | > > > | 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 | fossil_free(zToFree); fgetc(g.httpIn); /* Read past the "," separating header from content */ cgi_init(); } #if INTERFACE /* ** Bitmap values for the flags parameter to cgi_http_server(). */ #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ #define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */ #define HTTP_SERVER_HAD_REPOSITORY 0x0004 /* Was the repository open? */ #define HTTP_SERVER_HAD_CHECKOUT 0x0008 /* Was a checkout open? */ #define HTTP_SERVER_REPOLIST 0x0010 /* Allow repo listing */ #endif /* INTERFACE */ /* ** Maximum number of child processes that we can have running ** at one time before we start slowing things down. */ |
︙ | ︙ | |||
1427 1428 1429 1430 1431 1432 1433 | }else{ fossil_fatal("unable to open listening socket on any" " port in the range %d..%d", mnPort, mxPort); } } if( iPort>mxPort ) return 1; listen(listener,10); | < | | | < > | | | 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 | }else{ fossil_fatal("unable to open listening socket on any" " port in the range %d..%d", mnPort, mxPort); } } if( iPort>mxPort ) return 1; listen(listener,10); fossil_print("Listening for %s requests on TCP port %d\n", (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); fflush(stdout); if( zBrowser ){ assert( strstr(zBrowser,"%d")!=0 ); zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort); #if defined(__CYGWIN__) /* On Cygwin, we can do better than "echo" */ if( strncmp(zBrowser, "echo ", 5)==0 ){ wchar_t *wUrl = fossil_utf8_to_unicode(zBrowser+5); wUrl[wcslen(wUrl)-2] = 0; /* Strip terminating " &" */ if( (size_t)ShellExecuteW(0, L"open", wUrl, 0, 0, 1)<33 ){ fossil_warning("cannot start browser\n"); } }else #endif |
︙ | ︙ | |||
1475 1476 1477 1478 1479 1480 1481 | int nErr = 0, fd; close(0); fd = dup(connection); if( fd!=0 ) nErr++; close(1); fd = dup(connection); if( fd!=1 ) nErr++; | | | | 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 | int nErr = 0, fd; close(0); fd = dup(connection); if( fd!=0 ) nErr++; close(1); fd = dup(connection); if( fd!=1 ) nErr++; if( !g.fAnyTrace ){ close(2); fd = dup(connection); if( fd!=2 ) nErr++; } close(connection); return nErr; } } } /* Bury dead children */ while( waitpid(0, 0, WNOHANG)>0 ){ nchildren--; } } /* NOT REACHED */ fossil_exit(1); #endif /* NOT REACHED */ return 0; } |
︙ | ︙ | |||
1578 1579 1580 1581 1582 1583 1584 | }else if( p->tm_mon>11 ){ p->tm_year += p->tm_mon/12; p->tm_mon %= 12; } isLeapYr = p->tm_year%4==0 && (p->tm_year%100!=0 || (p->tm_year+300)%400==0); p->tm_yday = priorDays[p->tm_mon] + p->tm_mday - 1; if( isLeapYr && p->tm_mon>1 ) p->tm_yday++; | | > > > > > > > > > > > > > > > > > > | 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 | }else if( p->tm_mon>11 ){ p->tm_year += p->tm_mon/12; p->tm_mon %= 12; } isLeapYr = p->tm_year%4==0 && (p->tm_year%100!=0 || (p->tm_year+300)%400==0); p->tm_yday = priorDays[p->tm_mon] + p->tm_mday - 1; if( isLeapYr && p->tm_mon>1 ) p->tm_yday++; nDay = (p->tm_year-70)*365 + (p->tm_year-69)/4 -p->tm_year/100 + (p->tm_year+300)/400 + p->tm_yday; t = ((nDay*24 + p->tm_hour)*60 + p->tm_min)*60 + p->tm_sec; return t; } /* ** Check the objectTime against the If-Modified-Since request header. If the ** object time isn't any newer than the header, we immediately send back ** a 304 reply and exit. */ void cgi_modified_since(time_t objectTime){ const char *zIf = P("HTTP_IF_MODIFIED_SINCE"); if( zIf==0 ) return; if( objectTime > cgi_rfc822_parsedate(zIf) ) return; cgi_set_status(304,"Not Modified"); cgi_reset_content(); cgi_reply(); fossil_exit(0); } /* ** Check to see if the remote client is SSH and return ** its IP or return default */ const char *cgi_ssh_remote_addr(const char *zDefault){ char *zIndex; const char *zSshConn = fossil_getenv("SSH_CONNECTION"); if( zSshConn && zSshConn[0] ){ char *zSshClient = mprintf("%s",zSshConn); if( (zIndex = strchr(zSshClient,' '))!=0 ){ zSshClient[zIndex-zSshClient] = '\0'; return zSshClient; } } return zDefault; } |
Changes to src/checkin.c.
︙ | ︙ | |||
50 51 52 53 54 55 56 | Blob fname; file_tree_name(g.argv[i], &fname, 1); zName = blob_str(&fname); if( fossil_strcmp(zName, ".")==0 ) { blob_reset(&where); break; } | > | | | | | > | | | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | Blob fname; file_tree_name(g.argv[i], &fname, 1); zName = blob_str(&fname); if( fossil_strcmp(zName, ".")==0 ) { blob_reset(&where); break; } blob_append_sql(&where, " %s (pathname=%Q %s) " "OR (pathname>'%q/' %s AND pathname<'%q0' %s)", (blob_size(&where)>0) ? "OR" : "AND", zName, filename_collation(), zName, filename_collation(), zName, filename_collation() ); } db_prepare(&q, "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)" " FROM vfile " " WHERE is_selected(id) %s" " AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1 /*scan*/", blob_sql_text(&where) ); blob_zero(&rewrittenPathname); while( db_step(&q)==SQLITE_ROW ){ const char *zPathname = db_column_text(&q,0); const char *zDisplayName = zPathname; int isDeleted = db_column_int(&q, 1); int isChnged = db_column_int(&q,2); |
︙ | ︙ | |||
84 85 86 87 88 89 90 | zDisplayName += 2; /* no unnecessary ./ prefix */ } } blob_append(report, zPrefix, nPrefix); if( isDeleted ){ blob_appendf(report, "DELETED %s\n", zDisplayName); }else if( !file_wd_isfile_or_link(zFullName) ){ | | < < | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | zDisplayName += 2; /* no unnecessary ./ prefix */ } } blob_append(report, zPrefix, nPrefix); if( isDeleted ){ blob_appendf(report, "DELETED %s\n", zDisplayName); }else if( !file_wd_isfile_or_link(zFullName) ){ if( file_access(zFullName, F_OK)==0 ){ blob_appendf(report, "NOT_A_FILE %s\n", zDisplayName); if( missingIsFatal ){ fossil_warning("not a file: %s", zDisplayName); nErr++; } }else{ blob_appendf(report, "MISSING %s\n", zDisplayName); if( missingIsFatal ){ fossil_warning("missing file: %s", zDisplayName); nErr++; } } }else if( isNew ){ blob_appendf(report, "ADDED %s\n", zDisplayName); }else if( isChnged ){ if( isChnged==2 ){ blob_appendf(report, "UPDATED_BY_MERGE %s\n", zDisplayName); }else if( isChnged==3 ){ blob_appendf(report, "ADDED_BY_MERGE %s\n", zDisplayName); }else if( isChnged==4 ){ blob_appendf(report, "UPDATED_BY_INTEGRATE %s\n", zDisplayName); |
︙ | ︙ | |||
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | int relativePaths = db_get_boolean("relative-paths", 1); int absPathOption = find_option("abs-paths", 0, 0)!=0; int relPathOption = find_option("rel-paths", 0, 0)!=0; if( absPathOption ){ relativePaths = 0; } if( relPathOption ){ relativePaths = 1; } return relativePaths; } /* ** COMMAND: changes ** ** Usage: %fossil changes ?OPTIONS? ** ** Report on the edit status of all files in the current checkout. ** ** Pathnames are displayed according to the "relative-paths" setting, ** unless overridden by the --abs-paths or --rel-paths options. ** ** Options: ** --abs-paths Display absolute pathnames. ** --rel-paths Display pathnames relative to the current working ** directory. ** --sha1sum Verify file status using SHA1 hashing rather ** than relying on file mtimes. ** --header Identify the repository if there are changes ** -v|--verbose Say "(none)" if there are no changes ** | > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < | | < | | < < > | > > > > > > > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | > > > | > > > > > > > > > > > > | > | | | | | > | | > | | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | int relativePaths = db_get_boolean("relative-paths", 1); int absPathOption = find_option("abs-paths", 0, 0)!=0; int relPathOption = find_option("rel-paths", 0, 0)!=0; if( absPathOption ){ relativePaths = 0; } if( relPathOption ){ relativePaths = 1; } return relativePaths; } void print_changes( int useSha1sum, /* Verify file status using SHA1 hashing rather than relying on file mtimes. */ int showHdr, /* Identify the repository if there are changes */ int verboseFlag, /* Say "(none)" if there are no changes */ int cwdRelative /* Report relative to the current working dir */ ){ Blob report; int vid; blob_zero(&report); vid = db_lget_int("checkout", 0); vfile_check_signature(vid, useSha1sum ? CKSIG_SHA1 : 0); status_report(&report, "", 0, cwdRelative); if( verboseFlag && blob_size(&report)==0 ){ blob_append(&report, " (none)\n", -1); } if( showHdr && blob_size(&report)>0 ){ fossil_print("Changes for %s at %s:\n", db_get("project-name","???"), g.zLocalRoot); } blob_write_to_file(&report, "-"); blob_reset(&report); } /* ** COMMAND: changes ** ** Usage: %fossil changes ?OPTIONS? ** ** Report on the edit status of all files in the current checkout. ** ** Pathnames are displayed according to the "relative-paths" setting, ** unless overridden by the --abs-paths or --rel-paths options. ** ** Options: ** --abs-paths Display absolute pathnames. ** --rel-paths Display pathnames relative to the current working ** directory. ** --sha1sum Verify file status using SHA1 hashing rather ** than relying on file mtimes. ** --header Identify the repository if there are changes ** -v|--verbose Say "(none)" if there are no changes ** ** See also: extras, ls, status */ void changes_cmd(void){ int useSha1sum = find_option("sha1sum", 0, 0)!=0; int showHdr = find_option("header",0,0)!=0; int verboseFlag = find_option("verbose","v",0)!=0; int cwdRelative = 0; db_must_be_within_tree(); cwdRelative = determine_cwd_relative_option(); /* We should be done with options.. */ verify_all_options(); print_changes(useSha1sum, showHdr, verboseFlag, cwdRelative); } /* ** COMMAND: status ** ** Usage: %fossil status ?OPTIONS? ** ** Report on the status of the current checkout. ** ** Pathnames are displayed according to the "relative-paths" setting, ** unless overridden by the --abs-paths or --rel-paths options. ** ** Options: ** ** --abs-paths Display absolute pathnames. ** --rel-paths Display pathnames relative to the current working ** directory. ** --sha1sum Verify file status using SHA1 hashing rather ** than relying on file mtimes. ** ** See also: changes, extras, ls */ void status_cmd(void){ int vid; int useSha1sum = find_option("sha1sum", 0, 0)!=0; int showHdr = find_option("header",0,0)!=0; int verboseFlag = find_option("verbose","v",0)!=0; int cwdRelative = 0; db_must_be_within_tree(); /* 012345678901234 */ cwdRelative = determine_cwd_relative_option(); /* We should be done with options.. */ verify_all_options(); fossil_print("repository: %s\n", db_repository_filename()); fossil_print("local-root: %s\n", g.zLocalRoot); if( g.zConfigDbName ){ fossil_print("config-db: %s\n", g.zConfigDbName); } vid = db_lget_int("checkout", 0); if( vid ){ show_common_info(vid, "checkout:", 1, 1); } db_record_repository_filename(0); print_changes(useSha1sum, showHdr, verboseFlag, cwdRelative); } /* ** Take care of -r version of ls command */ static void ls_cmd_rev( const char *zRev, /* Revision string given */ int verboseFlag, /* Verbose flag given */ int showAge, /* Age flag given */ int timeOrder /* Order by time flag given */ ){ Stmt q; char *zOrderBy = "pathname COLLATE nocase"; char *zName; Blob where; int rid; int i; /* Handle given file names */ blob_zero(&where); for(i=2; i<g.argc; i++){ Blob fname; file_tree_name(g.argv[i], &fname, 1); zName = blob_str(&fname); if( fossil_strcmp(zName, ".")==0 ) { blob_reset(&where); break; } blob_append_sql(&where, " %s (pathname=%Q %s) " "OR (pathname>'%q/' %s AND pathname<'%q0' %s)", (blob_size(&where)>0) ? "OR" : "AND (", zName, filename_collation(), zName, filename_collation(), zName, filename_collation() ); } if( blob_size(&where)>0 ){ blob_append_sql(&where, ")"); } rid = symbolic_name_to_rid(zRev, "ci"); if( rid==0 ){ fossil_fatal("not a valid check-in: %s", zRev); } if( timeOrder ){ zOrderBy = "mtime DESC"; } compute_fileage(rid,0); db_prepare(&q, "SELECT datetime(fileage.mtime, 'localtime'), fileage.pathname,\n" " blob.size\n" " FROM fileage, blob\n" " WHERE blob.rid=fileage.fid %s\n" " ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/ ); blob_reset(&where); while( db_step(&q)==SQLITE_ROW ){ const char *zTime = db_column_text(&q,0); const char *zFile = db_column_text(&q,1); int size = db_column_int(&q,2); if( verboseFlag ){ fossil_print("%s %7d %s\n", zTime, size, zFile); }else if( showAge ){ fossil_print("%s %s\n", zTime, zFile); }else{ fossil_print("%s\n", zFile); } } db_finalize(&q); } /* ** COMMAND: ls ** ** Usage: %fossil ls ?OPTIONS? ?FILENAMES? ** ** Show the names of all files in the current checkout. The -v provides ** extra information about each file. If FILENAMES are included, only ** the files listed (or their children if they are directories) are shown. ** ** If -r is given a specific check-in is listed. In this case -R can be ** given to query another repository. ** ** Options: ** --age Show when each file was committed ** -v|--verbose Provide extra information about each file. ** -t Sort output in time order. ** -r VERSION The specific check-in to list ** -R|--repository FILE Extract info from repository FILE ** ** See also: changes, extras, status */ void ls_cmd(void){ int vid; Stmt q; int verboseFlag; int showAge; int timeOrder; char *zOrderBy = "pathname"; Blob where; int i; const char *zName; const char *zRev; verboseFlag = find_option("verbose","v", 0)!=0; if( !verboseFlag ){ verboseFlag = find_option("l","l", 0)!=0; /* deprecated */ } showAge = find_option("age",0,0)!=0; zRev = find_option("r","r",1); timeOrder = find_option("t","t",0)!=0; if( zRev!=0 ){ db_find_and_open_repository(0, 0); verify_all_options(); ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder); return; } db_must_be_within_tree(); vid = db_lget_int("checkout", 0); if( timeOrder ){ if( showAge ){ zOrderBy = mprintf("checkin_mtime(%d,rid) DESC", vid); }else{ zOrderBy = "mtime DESC"; } } verify_all_options(); blob_zero(&where); for(i=2; i<g.argc; i++){ Blob fname; file_tree_name(g.argv[i], &fname, 1); zName = blob_str(&fname); if( fossil_strcmp(zName, ".")==0 ) { blob_reset(&where); break; } blob_append_sql(&where, " %s (pathname=%Q %s) " "OR (pathname>'%q/' %s AND pathname<'%q0' %s)", (blob_size(&where)>0) ? "OR" : "WHERE", zName, filename_collation(), zName, filename_collation(), zName, filename_collation() ); } vfile_check_signature(vid, 0); if( showAge ){ db_prepare(&q, "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)," " datetime(checkin_mtime(%d,rid),'unixepoch'%s)" " FROM vfile %s" " ORDER BY %s", vid, timeline_utc(), blob_sql_text(&where), zOrderBy /*safe-for-%s*/ ); }else{ db_prepare(&q, "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)" " FROM vfile %s" " ORDER BY %s", blob_sql_text(&where), zOrderBy /*safe-for-%s*/ ); } blob_reset(&where); while( db_step(&q)==SQLITE_ROW ){ const char *zPathname = db_column_text(&q,0); int isDeleted = db_column_int(&q, 1); int isNew = db_column_int(&q,2)==0; int chnged = db_column_int(&q,3); int renamed = db_column_int(&q,4); char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); const char *type = ""; if( verboseFlag ){ if( isNew ){ type = "ADDED "; }else if( isDeleted ){ type = "DELETED "; }else if( !file_wd_isfile_or_link(zFullName) ){ if( file_access(zFullName, F_OK)==0 ){ type = "NOT_A_FILE "; }else{ type = "MISSING "; } }else if( chnged ){ if( chnged==2 ){ type = "UPDATED_BY_MERGE "; |
︙ | ︙ | |||
431 432 433 434 435 436 437 438 439 440 441 442 443 | ** Pathnames are displayed according to the "relative-paths" setting, ** unless overridden by the --abs-paths or --rel-paths options. ** ** Options: ** --abs-paths Display absolute pathnames. ** --case-sensitive <BOOL> override case-sensitive setting ** --dotfiles include files beginning with a dot (".") ** --ignore <CSG> ignore files matching patterns from the argument ** --rel-paths Display pathnames relative to the current working ** directory. ** ** See also: changes, clean, status */ | > | > < > > > > > > > > > > > > | 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 | ** Pathnames are displayed according to the "relative-paths" setting, ** unless overridden by the --abs-paths or --rel-paths options. ** ** Options: ** --abs-paths Display absolute pathnames. ** --case-sensitive <BOOL> override case-sensitive setting ** --dotfiles include files beginning with a dot (".") ** --header Identify the repository if there are extras ** --ignore <CSG> ignore files matching patterns from the argument ** --rel-paths Display pathnames relative to the current working ** directory. ** ** See also: changes, clean, status */ void extras_cmd(void){ Stmt q; const char *zIgnoreFlag = find_option("ignore",0,1); unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; int showHdr = find_option("header",0,0)!=0; int cwdRelative = 0; Glob *pIgnore; Blob rewrittenPathname; const char *zPathname, *zDisplayName; if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; db_must_be_within_tree(); cwdRelative = determine_cwd_relative_option(); if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL; /* We should be done with options.. */ verify_all_options(); if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } pIgnore = glob_create(zIgnoreFlag); locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); glob_free(pIgnore); db_prepare(&q, "SELECT x FROM sfile" " WHERE x NOT IN (%s)" " ORDER BY 1", fossil_all_reserved_names(0) ); db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); blob_zero(&rewrittenPathname); g.allowSymlinks = 1; /* Report on symbolic links */ while( db_step(&q)==SQLITE_ROW ){ zDisplayName = zPathname = db_column_text(&q, 0); if( cwdRelative ) { char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); file_relative_name(zFullName, &rewrittenPathname, 0); free(zFullName); zDisplayName = blob_str(&rewrittenPathname); if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){ zDisplayName += 2; /* no unnecessary ./ prefix */ } } if( showHdr ){ showHdr = 0; fossil_print("Extras for %s at %s:\n", db_get("project-name","???"), g.zLocalRoot); } fossil_print("%s\n", zDisplayName); } blob_reset(&rewrittenPathname); db_finalize(&q); } /* |
︙ | ︙ | |||
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 | ** explicitly exempted via the empty-dirs setting ** or another applicable setting or command line ** argument. Matching files, if any, are removed ** prior to checking for any empty directories; ** therefore, directories that contain only files ** that were removed will be removed as well. ** -f|--force Remove files without prompting. ** --clean <CSG> Never prompt for files matching this ** comma separated list of glob patterns. ** --ignore <CSG> Ignore files matching patterns from the ** comma separated list of glob patterns. ** --keep <CSG> Keep files matching this comma separated ** list of glob patterns. ** -n|--dry-run If given, display instead of run actions. ** --temp Remove only Fossil-generated temporary files. ** -v|--verbose Show all files as they are removed. ** | > | > > > < > > > > > > > | > > > > > > > < | 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 | ** explicitly exempted via the empty-dirs setting ** or another applicable setting or command line ** argument. Matching files, if any, are removed ** prior to checking for any empty directories; ** therefore, directories that contain only files ** that were removed will be removed as well. ** -f|--force Remove files without prompting. ** --verily Shorthand for: -f --emptydirs --dotfiles ** --clean <CSG> Never prompt for files matching this ** comma separated list of glob patterns. ** --ignore <CSG> Ignore files matching patterns from the ** comma separated list of glob patterns. ** --keep <CSG> Keep files matching this comma separated ** list of glob patterns. ** -n|--dry-run If given, display instead of run actions. ** --temp Remove only Fossil-generated temporary files. ** -v|--verbose Show all files as they are removed. ** ** See also: addremove, extras, status */ void clean_cmd(void){ int allFileFlag, allDirFlag, dryRunFlag, verboseFlag; int emptyDirsFlag, dirsOnlyFlag; unsigned scanFlags = 0; const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag; Glob *pIgnore, *pKeep, *pClean; int nRoot; dryRunFlag = find_option("dry-run","n",0)!=0; if( !dryRunFlag ){ dryRunFlag = find_option("test",0,0)!=0; /* deprecated */ } if( !dryRunFlag ){ dryRunFlag = find_option("whatif",0,0)!=0; } allFileFlag = allDirFlag = find_option("force","f",0)!=0; dirsOnlyFlag = find_option("dirsonly",0,0)!=0; emptyDirsFlag = find_option("emptydirs","d",0)!=0 || dirsOnlyFlag; if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL; if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; if( find_option("allckouts",0,0)!=0 ) scanFlags |= SCAN_NESTED; zIgnoreFlag = find_option("ignore",0,1); verboseFlag = find_option("verbose","v",0)!=0; zKeepFlag = find_option("keep",0,1); zCleanFlag = find_option("clean",0,1); db_must_be_within_tree(); if( find_option("verily",0,0)!=0 ){ allFileFlag = allDirFlag = 1; emptyDirsFlag = 1; scanFlags |= SCAN_ALL; } if( zIgnoreFlag==0 ){ zIgnoreFlag = db_get("ignore-glob", 0); } if( zKeepFlag==0 ){ zKeepFlag = db_get("keep-glob", 0); } if( zCleanFlag==0 ){ zCleanFlag = db_get("clean-glob", 0); } if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL; verify_all_options(); pIgnore = glob_create(zIgnoreFlag); pKeep = glob_create(zKeepFlag); pClean = glob_create(zCleanFlag); nRoot = (int)strlen(g.zLocalRoot); g.allowSymlinks = 1; /* Find symlinks too */ if( !dirsOnlyFlag ){ Stmt q; Blob repo; locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); db_prepare(&q, "SELECT %Q || x FROM sfile" " WHERE x NOT IN (%s)" " ORDER BY 1", g.zLocalRoot, fossil_all_reserved_names(0) ); if( file_tree_name(g.zRepositoryName, &repo, 0) ){ db_multi_exec("DELETE FROM sfile WHERE x=%B", &repo); } db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); if( glob_match(pKeep, zName+nRoot) ){ if( verboseFlag ){ fossil_print("KEPT file \"%s\" not removed (due to --keep" " or \"keep-glob\")\n", zName+nRoot); } continue; } if( !allFileFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ Blob ans; char cReply; char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ", zName+nRoot); prompt_user(prompt, &ans); cReply = blob_str(&ans)[0]; if( cReply=='a' || cReply=='A' ){ allFileFlag = 1; }else if( cReply!='y' && cReply!='Y' ){ blob_reset(&ans); continue; |
︙ | ︙ | |||
621 622 623 624 625 626 627 | db_finalize(&q); } if( emptyDirsFlag ){ Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); Stmt q; Blob root; blob_init(&root, g.zLocalRoot, nRoot - 1); | | | > > > > > > > < | 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 | db_finalize(&q); } if( emptyDirsFlag ){ Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0)); Stmt q; Blob root; blob_init(&root, g.zLocalRoot, nRoot - 1); vfile_dir_scan(&root, blob_size(&root), scanFlags, pIgnore, pEmptyDirs, 0); blob_reset(&root); db_prepare(&q, "SELECT %Q || x FROM dscan_temp" " WHERE x NOT IN (%s) AND y = 0" " ORDER BY 1 DESC", g.zLocalRoot, fossil_all_reserved_names(0) ); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); if( glob_match(pKeep, zName+nRoot) ){ if( verboseFlag ){ fossil_print("KEPT directory \"%s\" not removed (due to --keep" " or \"keep-glob\")\n", zName+nRoot); } continue; } if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){ Blob ans; char cReply; char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ", zName+nRoot); prompt_user(prompt, &ans); cReply = blob_str(&ans)[0]; if( cReply=='a' || cReply=='A' ){ allDirFlag = 1; }else if( cReply!='y' && cReply!='Y' ){ blob_reset(&ans); continue; |
︙ | ︙ | |||
795 796 797 798 799 800 801 | blob_append(&prompt, zInit, -1); } #else blob_init(&prompt, zInit, -1); #endif blob_append(&prompt, "\n" | | | > > > > > > > > > > > > | > > > > > > > > | 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 | blob_append(&prompt, zInit, -1); } #else blob_init(&prompt, zInit, -1); #endif blob_append(&prompt, "\n" "# Enter a commit message for this check-in. Lines beginning with # are ignored.\n" "#\n", -1 ); blob_appendf(&prompt, "# user: %s\n", p->zUserOvrd ? p->zUserOvrd : login_name()); if( p->zBranch && p->zBranch[0] ){ blob_appendf(&prompt, "# tags: %s\n#\n", p->zBranch); }else{ char *zTags = info_tags_of_checkin(parent_rid, 1); if( zTags || p->azTag ){ blob_append(&prompt, "# tags: ", 8); if(zTags){ blob_appendf(&prompt, "%z%s", zTags, p->azTag ? ", " : ""); } if(p->azTag){ int i = 0; for( ; p->azTag[i]; ++i ){ blob_appendf(&prompt, "%s%s", p->azTag[i], p->azTag[i+1] ? ", " : ""); } } blob_appendf(&prompt, "\n#\n"); } } status_report(&prompt, "# ", 1, 0); if( g.markPrivate ){ blob_append(&prompt, "# PRIVATE BRANCH: This check-in will be private and will not sync to\n" "# repositories.\n" "#\n", -1 ); } if( p->integrateFlag ){ blob_append(&prompt, "#\n" "# All merged-in branches will be closed due to the --integrate flag\n" "#\n", -1 ); } prompt_for_user_comment(pComment, &prompt); blob_reset(&prompt); } /* ** Populate the Global.aCommitFile[] based on the command line arguments |
︙ | ︙ | |||
840 841 842 843 844 845 846 | int select_commit_files(void){ int result = 0; assert( g.aCommitFile==0 ); if( g.argc>2 ){ int ii, jj=0; Blob fname; Stmt q; | < < | | | 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | int select_commit_files(void){ int result = 0; assert( g.aCommitFile==0 ); if( g.argc>2 ){ int ii, jj=0; Blob fname; Stmt q; Bag toCommit; blob_zero(&fname); bag_init(&toCommit); for(ii=2; ii<g.argc; ii++){ int cnt = 0; file_tree_name(g.argv[ii], &fname, 1); if( fossil_strcmp(blob_str(&fname),".")==0 ){ bag_clear(&toCommit); return result; } db_prepare(&q, "SELECT id FROM vfile WHERE pathname=%Q %s" " OR (pathname>'%q/' %s AND pathname<'%q0' %s)", blob_str(&fname), filename_collation(), blob_str(&fname), filename_collation(), blob_str(&fname), filename_collation()); while( db_step(&q)==SQLITE_ROW ){ cnt++; bag_insert(&toCommit, db_column_int(&q, 0)); } db_finalize(&q); if( cnt==0 ){ fossil_warning("fossil knows nothing about: %s", g.argv[ii]); |
︙ | ︙ | |||
897 898 899 900 901 902 903 | b = db_exists( "SELECT 1 FROM event" " WHERE datetime(mtime)>=%Q" " AND type='ci' AND objid=%d", zDate, rid ); if( b ){ | | | 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 | b = db_exists( "SELECT 1 FROM event" " WHERE datetime(mtime)>=%Q" " AND type='ci' AND objid=%d", zDate, rid ); if( b ){ fossil_fatal("ancestor check-in [%S] (%s) is not older (clock skew?)" " Use --allow-older to override.", zUuid, zDate); } #endif } /* ** zDate should be a valid date string. Convert this string into the |
︙ | ︙ | |||
950 951 952 953 954 955 956 957 958 | ** check-in manifest. */ struct CheckinInfo { Blob *pComment; /* Check-in comment text */ const char *zMimetype; /* Mimetype of check-in command. May be NULL */ int verifyDate; /* Verify that child is younger */ int closeFlag; /* Close the branch being committed */ Blob *pCksum; /* Repository checksum. May be 0 */ const char *zDateOvrd; /* Date override. If 0 then use 'now' */ | > | | > | > > > > > > > | 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 | ** check-in manifest. */ struct CheckinInfo { Blob *pComment; /* Check-in comment text */ const char *zMimetype; /* Mimetype of check-in command. May be NULL */ int verifyDate; /* Verify that child is younger */ int closeFlag; /* Close the branch being committed */ int integrateFlag; /* Close merged-in branches */ Blob *pCksum; /* Repository checksum. May be 0 */ const char *zDateOvrd; /* Date override. If 0 then use 'now' */ const char *zUserOvrd; /* User override. If 0 then use login_name() */ const char *zBranch; /* Branch name. May be 0 */ const char *zColor; /* One-time background color. May be 0 */ const char *zBrClr; /* Persistent branch color. May be 0 */ const char **azTag; /* Tags to apply to this check-in */ }; #endif /* INTERFACE */ /* ** Create a manifest. */ static void create_manifest( Blob *pOut, /* Write the manifest here */ const char *zBaselineUuid, /* UUID of baseline, or zero */ Manifest *pBaseline, /* Make it a delta manifest if not zero */ int vid, /* BLOB.id for the parent check-in */ CheckinInfo *p, /* Information about the check-in */ int *pnFBcard /* OUT: Number of generated B- and F-cards */ ){ char *zDate; /* Date of the check-in */ char *zParentUuid = 0; /* UUID of parent check-in */ Blob filename; /* A single filename */ int nBasename; /* Size of base filename */ Stmt q; /* Various queries */ Blob mcksum; /* Manifest checksum */ ManifestFile *pFile; /* File from the baseline */ int nFBcard = 0; /* Number of B-cards and F-cards */ int i; /* Loop counter */ const char *zColor; /* Modified value of p->zColor */ assert( pBaseline==0 || pBaseline->zBaseline==0 ); assert( pBaseline==0 || zBaselineUuid!=0 ); blob_zero(pOut); if( vid ){ zParentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d AND " "EXISTS(SELECT 1 FROM event WHERE event.type='ci' and event.objid=%d)", vid, vid); if( !zParentUuid ){ fossil_fatal("Could not find a valid check-in for RID %d. " "Possible checkout/repo mismatch.", vid); } } if( pBaseline ){ blob_appendf(pOut, "B %s\n", zBaselineUuid); manifest_file_rewind(pBaseline); pFile = manifest_file_next(pBaseline, 0); nFBcard++; }else{ pFile = 0; |
︙ | ︙ | |||
1080 1081 1082 1083 1084 1085 1086 | blob_appendf(pOut, "F %F\n", pFile->zName); pFile = manifest_file_next(pBaseline, 0); nFBcard++; } if( p->zMimetype && p->zMimetype[0] ){ blob_appendf(pOut, "N %F\n", p->zMimetype); } | > | | | | | | | | | | | | | | | | < | > > | 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 | blob_appendf(pOut, "F %F\n", pFile->zName); pFile = manifest_file_next(pBaseline, 0); nFBcard++; } if( p->zMimetype && p->zMimetype[0] ){ blob_appendf(pOut, "N %F\n", p->zMimetype); } if( vid ){ blob_appendf(pOut, "P %s", zParentUuid); if( p->verifyDate ) checkin_verify_younger(vid, zParentUuid, zDate); free(zParentUuid); db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0 OR id<-2"); while( db_step(&q)==SQLITE_ROW ){ char *zMergeUuid; int mid = db_column_int(&q, 0); if( (!g.markPrivate && content_is_private(mid)) || (mid == vid) ) continue; zMergeUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); if( zMergeUuid ){ blob_appendf(pOut, " %s", zMergeUuid); if( p->verifyDate ) checkin_verify_younger(mid, zMergeUuid, zDate); free(zMergeUuid); } } db_finalize(&q); blob_appendf(pOut, "\n"); } free(zDate); db_prepare(&q, "SELECT CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || blob.uuid, merge" " FROM vmerge, blob" " WHERE (vmerge.id=-1 OR vmerge.id=-2)" " AND blob.rid=vmerge.merge" " ORDER BY 1"); |
︙ | ︙ | |||
1133 1134 1135 1136 1137 1138 1139 | /* One-time background color */ blob_appendf(pOut, "T +bgcolor * %F\n", zColor); } if( p->closeFlag ){ blob_appendf(pOut, "T +closed *\n"); } db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid" | | > | 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 | /* One-time background color */ blob_appendf(pOut, "T +bgcolor * %F\n", zColor); } if( p->closeFlag ){ blob_appendf(pOut, "T +closed *\n"); } db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid" " WHERE id %s ORDER BY 1", p->integrateFlag ? "IN(0,-4)" : "=(-4)"); while( db_step(&q)==SQLITE_ROW ){ const char *zIntegrateUuid = db_column_text(&q, 0); int rid = db_column_int(&q, 1); if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref " " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){ blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid); } |
︙ | ︙ | |||
1167 1168 1169 1170 1171 1172 1173 | vid, p->zBranch); while( db_step(&q)==SQLITE_ROW ){ const char *zBrTag = db_column_text(&q, 0); blob_appendf(pOut, "T -%F *\n", zBrTag); } db_finalize(&q); } | | | 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 | vid, p->zBranch); while( db_step(&q)==SQLITE_ROW ){ const char *zBrTag = db_column_text(&q, 0); blob_appendf(pOut, "T -%F *\n", zBrTag); } db_finalize(&q); } blob_appendf(pOut, "U %F\n", p->zUserOvrd ? p->zUserOvrd : login_name()); md5sum_blob(pOut, &mcksum); blob_appendf(pOut, "Z %b\n", &mcksum); if( pnFBcard ) *pnFBcard = nFBcard; } /* ** Issue a warning and give the user an opportunity to abandon out |
︙ | ︙ | |||
1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 | int bReverse; /* UTF-16 byte order is reversed? */ int fUnicode; /* return value of could_be_utf16() */ int fBinary; /* does the blob content appear to be binary? */ int lookFlags; /* output flags from looks_like_utf8/utf16() */ int fHasAnyCr; /* the blob contains one or more CR chars */ int fHasLoneCrOnly; /* all detected line endings are CR only */ int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */ char *zMsg; /* Warning message */ Blob fname; /* Relative pathname of the file */ static int allOk = 0; /* Set to true to disable this routine */ if( allOk ) return 0; fUnicode = could_be_utf16(p, &bReverse); if( fUnicode ){ lookFlags = looks_like_utf16(p, bReverse, LOOK_NUL); }else{ lookFlags = looks_like_utf8(p, LOOK_NUL); } fHasAnyCr = (lookFlags & LOOK_CR); fBinary = (lookFlags & LOOK_BINARY); fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR); fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF); | > > > > | | 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 | int bReverse; /* UTF-16 byte order is reversed? */ int fUnicode; /* return value of could_be_utf16() */ int fBinary; /* does the blob content appear to be binary? */ int lookFlags; /* output flags from looks_like_utf8/utf16() */ int fHasAnyCr; /* the blob contains one or more CR chars */ int fHasLoneCrOnly; /* all detected line endings are CR only */ int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */ int fHasInvalidUtf8 = 0;/* contains byte-sequence which is invalid for UTF-8 */ char *zMsg; /* Warning message */ Blob fname; /* Relative pathname of the file */ static int allOk = 0; /* Set to true to disable this routine */ if( allOk ) return 0; fUnicode = could_be_utf16(p, &bReverse); if( fUnicode ){ lookFlags = looks_like_utf16(p, bReverse, LOOK_NUL); }else{ lookFlags = looks_like_utf8(p, LOOK_NUL); if( !(lookFlags & LOOK_BINARY) && invalid_utf8(p) ){ fHasInvalidUtf8 = 1; } } fHasAnyCr = (lookFlags & LOOK_CR); fBinary = (lookFlags & LOOK_BINARY); fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR); fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF); if( fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8){ const char *zWarning; const char *zDisable; const char *zConvert = "c=convert/"; Blob ans; char cReply; if( fBinary ){ |
︙ | ︙ | |||
1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 | zWarning = "CR line endings and Unicode"; }else if( fHasCrLfOnly ){ zWarning = "CR/NL line endings and Unicode"; }else{ zWarning = "mixed line endings and Unicode"; } zDisable = "\"crnl-glob\" and \"encoding-glob\" settings"; }else if( fHasAnyCr ){ if( crnlOk ){ return 0; /* We don't want CR/NL warnings for this file. */ } if( fHasLoneCrOnly ){ zWarning = "CR line endings"; }else if( fHasCrLfOnly ){ zWarning = "CR/NL line endings"; }else{ zWarning = "mixed line endings"; } zDisable = "\"crnl-glob\" setting"; }else{ if( encodingOk ){ return 0; /* We don't want encoding warnings for this file. */ } zWarning = "Unicode"; | > > > > > > < < < < > > > | | | | | > > | | | | | | > | 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 | zWarning = "CR line endings and Unicode"; }else if( fHasCrLfOnly ){ zWarning = "CR/NL line endings and Unicode"; }else{ zWarning = "mixed line endings and Unicode"; } zDisable = "\"crnl-glob\" and \"encoding-glob\" settings"; }else if( fHasInvalidUtf8 ){ if( encodingOk ){ return 0; /* We don't want encoding warnings for this file. */ } zWarning = "invalid UTF-8"; zDisable = "\"encoding-glob\" setting"; }else if( fHasAnyCr ){ if( crnlOk ){ return 0; /* We don't want CR/NL warnings for this file. */ } if( fHasLoneCrOnly ){ zWarning = "CR line endings"; }else if( fHasCrLfOnly ){ zWarning = "CR/NL line endings"; }else{ zWarning = "mixed line endings"; } zDisable = "\"crnl-glob\" setting"; }else{ if( encodingOk ){ return 0; /* We don't want encoding warnings for this file. */ } zWarning = "Unicode"; zDisable = "\"encoding-glob\" setting"; } file_relative_name(zFilename, &fname, 0); zMsg = mprintf( "%s contains %s. Use --no-warnings or the %s to disable this warning.\n" "Commit anyhow (a=all/%sy/N)? ", blob_str(&fname), zWarning, zDisable, zConvert); prompt_user(zMsg, &ans); fossil_free(zMsg); cReply = blob_str(&ans)[0]; if( cReply=='a' || cReply=='A' ){ allOk = 1; }else if( *zConvert && (cReply=='c' || cReply=='C') ){ char *zOrig = file_newname(zFilename, "original", 1); FILE *f; blob_write_to_file(p, zOrig); fossil_free(zOrig); f = fossil_fopen(zFilename, "wb"); if( f==0 ){ fossil_warning("cannot open %s for writing", zFilename); }else{ if( fUnicode ) { int bomSize; const unsigned char *bom = get_utf8_bom(&bomSize); fwrite(bom, 1, bomSize, f); blob_to_utf8_no_bom(p, 0); }else if( fHasInvalidUtf8 ){ blob_cp1252_to_utf8(p); } if( fHasAnyCr ){ blob_to_lf_only(p); } fwrite(blob_buffer(p), 1, blob_size(p), f); fclose(f); } return 1; }else if( cReply!='y' && cReply!='Y' ){ fossil_fatal("Abandoning commit due to %s in %s", zWarning, blob_str(&fname)); } blob_reset(&ans); blob_reset(&fname); |
︙ | ︙ | |||
1338 1339 1340 1341 1342 1343 1344 | ** The --branch option followed by a branch name causes the new ** check-in to be placed in a newly-created branch with the name ** passed to the --branch option. ** ** Use the --branchcolor option followed by a color name (ex: ** '#ffc0c0') to specify the background color of entries in the new ** branch when shown in the web timeline interface. The use of | | > > > > | > > > > > > | | > | 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 | ** The --branch option followed by a branch name causes the new ** check-in to be placed in a newly-created branch with the name ** passed to the --branch option. ** ** Use the --branchcolor option followed by a color name (ex: ** '#ffc0c0') to specify the background color of entries in the new ** branch when shown in the web timeline interface. The use of ** the --branchcolor option is not recommended. Instead, let Fossil ** choose the branch color automatically. ** ** The --bgcolor option works like --branchcolor but only sets the ** background color for a single check-in. Subsequent check-ins revert ** to the default color. ** ** A check-in is not permitted to fork unless the --allow-fork option ** appears. An empty check-in (i.e. with nothing changed) is not ** allowed unless the --allow-empty option appears. A check-in may not ** be older than its ancestor unless the --allow-older option appears. ** If any of files in the check-in appear to contain unresolved merge ** conflicts, the check-in will not be allowed unless the ** --allow-conflict option is present. In addition, the entire ** check-in process may be aborted if a file contains content that ** appears to be binary, Unicode text, or text with CR/NL line endings ** unless the interactive user chooses to proceed. If there is no ** interactive user or these warnings should be skipped for some other ** reason, the --no-warnings option may be used. A check-in is not ** allowed against a closed leaf. ** ** If a commit message is blank, you will be prompted: ** ("continue (y/N)?") to confirm you really want to commit with a ** blank commit message. The default value is "N", do not commit. ** ** The --private option creates a private check-in that is never synced. ** Children of private check-ins are automatically private. ** ** The --tag option applies the symbolic tag name to the check-in. ** ** The --sha1sum option detects edited files by computing each file's ** SHA1 hash rather than just checking for changes to its size or mtime. ** ** Options: ** --allow-conflict allow unresolved merge conflicts ** --allow-empty allow a commit with no changes ** --allow-fork allow the commit to fork ** --allow-older allow a commit older than its ancestor ** --baseline use a baseline manifest in the commit process ** --bgcolor COLOR apply COLOR to this one check-in only ** --branch NEW-BRANCH-NAME check in to this new branch ** --branchcolor COLOR apply given COLOR to the branch ** --close close the branch being committed ** --delta use a delta manifest in the commit process ** --integrate close all merged-in branches ** -m|--comment COMMENT-TEXT use COMMENT-TEXT as commit comment ** -M|--message-file FILE read the commit comment from given file ** --mimetype MIMETYPE mimetype of check-in comment ** -n|--dry-run If given, display instead of run actions ** --no-warnings omit all warnings about file contents ** --nosign do not attempt to sign this commit with gpg ** --private do not sync changes and their descendants ** --sha1sum verify file status using SHA1 hashing rather ** than relying on file mtimes ** --tag TAG-NAME assign given tag TAG-NAME to the check-in ** ** See also: branch, changes, checkout, extras, sync */ void commit_cmd(void){ int hasChanges; /* True if unsaved changes exist */ int vid; /* blob-id of parent version */ int nrid; /* blob-id of a modified file */ int nvid; /* Blob-id of the new check-in */ Blob comment; /* Check-in comment */ const char *zComment; /* Check-in comment */ Stmt q; /* Various queries */ char *zUuid; /* UUID of the new check-in */ int useSha1sum = 0; /* True to verify file status using SHA1 hashing */ int noSign = 0; /* True to omit signing the manifest using GPG */ int isAMerge = 0; /* True if checking in a merge */ int noWarningFlag = 0; /* True if skipping all warnings */ int forceFlag = 0; /* Undocumented: Disables all checks */ int forceDelta = 0; /* Force a delta-manifest */ int forceBaseline = 0; /* Force a baseline-manifest */ int allowConflict = 0; /* Allow unresolve merge conflicts */ |
︙ | ︙ | |||
1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 | int nConflict = 0; /* Number of unresolved merge conflicts */ int abortCommit = 0; Blob ans; char cReply; memset(&sCiInfo, 0, sizeof(sCiInfo)); url_proxy_options(); noSign = find_option("nosign",0,0)!=0; forceDelta = find_option("delta",0,0)!=0; forceBaseline = find_option("baseline",0,0)!=0; if( forceDelta && forceBaseline ){ fossil_fatal("cannot use --delta and --baseline together"); } dryRunFlag = find_option("dry-run","n",0)!=0; | > | 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 | int nConflict = 0; /* Number of unresolved merge conflicts */ int abortCommit = 0; Blob ans; char cReply; memset(&sCiInfo, 0, sizeof(sCiInfo)); url_proxy_options(); useSha1sum = find_option("sha1sum", 0, 0)!=0; noSign = find_option("nosign",0,0)!=0; forceDelta = find_option("delta",0,0)!=0; forceBaseline = find_option("baseline",0,0)!=0; if( forceDelta && forceBaseline ){ fossil_fatal("cannot use --delta and --baseline together"); } dryRunFlag = find_option("dry-run","n",0)!=0; |
︙ | ︙ | |||
1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 | allowFork = find_option("allow-fork",0,0)!=0; allowOlder = find_option("allow-older",0,0)!=0; noWarningFlag = find_option("no-warnings", 0, 0)!=0; sCiInfo.zBranch = find_option("branch","b",1); sCiInfo.zColor = find_option("bgcolor",0,1); sCiInfo.zBrClr = find_option("branchcolor",0,1); sCiInfo.closeFlag = find_option("close",0,0)!=0; sCiInfo.zMimetype = find_option("mimetype",0,1); while( (zTag = find_option("tag",0,1))!=0 ){ if( zTag[0]==0 ) continue; sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, sizeof(char*)*(nTag+2)); sCiInfo.azTag[nTag++] = zTag; sCiInfo.azTag[nTag] = 0; } | > | 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 | allowFork = find_option("allow-fork",0,0)!=0; allowOlder = find_option("allow-older",0,0)!=0; noWarningFlag = find_option("no-warnings", 0, 0)!=0; sCiInfo.zBranch = find_option("branch","b",1); sCiInfo.zColor = find_option("bgcolor",0,1); sCiInfo.zBrClr = find_option("branchcolor",0,1); sCiInfo.closeFlag = find_option("close",0,0)!=0; sCiInfo.integrateFlag = find_option("integrate",0,0)!=0; sCiInfo.zMimetype = find_option("mimetype",0,1); while( (zTag = find_option("tag",0,1))!=0 ){ if( zTag[0]==0 ) continue; sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, sizeof(char*)*(nTag+2)); sCiInfo.azTag[nTag++] = zTag; sCiInfo.azTag[nTag] = 0; } |
︙ | ︙ | |||
1490 1491 1492 1493 1494 1495 1496 | */ if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ forceBaseline = 1; } /* Get the ID of the parent manifest artifact */ vid = db_lget_int("checkout", 0); | > > | | < < < | | 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 | */ if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){ forceBaseline = 1; } /* Get the ID of the parent manifest artifact */ vid = db_lget_int("checkout", 0); if( vid==0 ){ useCksum = 1; }else if( content_is_private(vid) ){ g.markPrivate = 1; } /* ** Autosync if autosync is enabled and this is not a private check-in. */ if( !g.markPrivate ){ if( autosync_loop(SYNC_PULL, db_get_int("autosync-tries", 1)) ){ prompt_user("continue in spite of sync failure (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' ){ fossil_exit(1); } } } /* Require confirmation to continue with the check-in if there is ** clock skew */ if( g.clockSkewSeen ){ prompt_user("continue in spite of time skew (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' ){ fossil_exit(1); } } /* There are two ways this command may be executed. If there are ** no arguments following the word "commit", then all modified files ** in the checked out directory are committed. If one or more arguments ** follows "commit", then only those files are committed. ** ** After the following function call has returned, the Global.aCommitFile[] ** array is allocated to contain the "id" field from the vfile table ** for each file to be committed. Or, if aCommitFile is NULL, all files ** should be committed. */ if( select_commit_files() ){ prompt_user("continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' ) fossil_exit(1); } isAMerge = db_exists("SELECT 1 FROM vmerge WHERE id=0 OR id<-2"); if( g.aCommitFile && isAMerge ){ fossil_fatal("cannot do a partial commit of a merge"); } /* Doing "fossil mv fileA fileB; fossil add fileA; fossil commit fileA" |
︙ | ︙ | |||
1571 1572 1573 1574 1575 1576 1577 | /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); } | | | 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 | /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); } hasChanges = unsaved_changes(useSha1sum ? CKSIG_SHA1 : 0); db_begin_transaction(); db_record_repository_filename(0); if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){ fossil_fatal("nothing has changed; use --allow-empty to override"); } /* If none of the files that were named on the command line have |
︙ | ︙ | |||
1597 1598 1599 1600 1601 1602 1603 1604 | fossil_fatal("none of the selected files have changed; use " "--allow-empty to override."); } /* ** Do not allow a commit that will cause a fork unless the --allow-fork ** or --force flags is used, or unless this is a private check-in. */ | > > > > > > > > > | | > > > | | > > > > > > > < | < | 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 | fossil_fatal("none of the selected files have changed; use " "--allow-empty to override."); } /* ** Do not allow a commit that will cause a fork unless the --allow-fork ** or --force flags is used, or unless this is a private check-in. ** The initial commit MUST have tags "trunk" and "sym-trunk". */ if( !vid ){ if( sCiInfo.zBranch==0 ){ if( allowFork==0 && forceFlag==0 && g.markPrivate==0 && db_exists("SELECT 1 from event where type='ci'") ){ fossil_fatal("would fork. \"update\" first or use --allow-fork."); } sCiInfo.zBranch = db_get("main-branch", "trunk"); } }else if( sCiInfo.zBranch==0 && allowFork==0 && forceFlag==0 && g.markPrivate==0 && !is_a_leaf(vid) ){ fossil_fatal("would fork. \"update\" first or use --allow-fork."); } /* ** Do not allow a commit against a closed leaf unless the commit ** ends up on a different branch. */ if( /* parent check-in has the "closed" tag... */ db_exists("SELECT 1 FROM tagxref" " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, vid) /* ... and the new check-in has no --branch option or the --branch ** option does not actually change the branch */ && (sCiInfo.zBranch==0 || db_exists("SELECT 1 FROM tagxref" " WHERE tagid=%d AND rid=%d AND tagtype>0" " AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch)) ){ fossil_fatal("cannot commit against a closed leaf"); } if( useCksum ) vfile_aggregate_checksum_disk(vid, &cksum1); if( zComment ){ blob_zero(&comment); blob_append(&comment, zComment, -1); }else if( zComFile ){ blob_zero(&comment); blob_read_from_file(&comment, zComFile); blob_to_utf8_no_bom(&comment, 1); }else if(dryRunFlag){ blob_zero(&comment); }else{ char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'"); prepare_commit_comment(&comment, zInit, &sCiInfo, vid); if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ prompt_user("unchanged check-in comment. continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' ) fossil_exit(1); } free(zInit); } if( blob_size(&comment)==0 ){ if( !dryRunFlag ){ prompt_user("empty check-in comment. continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' ){ fossil_exit(1); } } }else{ |
︙ | ︙ | |||
1741 1742 1743 1744 1745 1746 1747 | pBaseline = pParent; } if( pBaseline ){ Blob delta; create_manifest(&delta, zBaselineUuid, pBaseline, vid, &sCiInfo, &szD); /* ** At this point, two manifests have been constructed, either of | | | 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 | pBaseline = pParent; } if( pBaseline ){ Blob delta; create_manifest(&delta, zBaselineUuid, pBaseline, vid, &sCiInfo, &szD); /* ** At this point, two manifests have been constructed, either of ** which would work for this check-in. The first manifest (held ** in the "manifest" variable) is a baseline manifest and the second ** (held in variable named "delta") is a delta manifest. The ** question now is: which manifest should we use? ** ** Let B be the number of F-cards in the baseline manifest and ** let D be the number of F-cards in the delta manifest, plus one for ** the B-card. (B is held in the szB variable and D is held in the |
︙ | ︙ | |||
1769 1770 1771 1772 1773 1774 1775 | blob_reset(&delta); } }else if( forceDelta ){ fossil_fatal("unable to find a baseline-manifest for the delta"); } } if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){ | < | 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 | blob_reset(&delta); } }else if( forceDelta ){ fossil_fatal("unable to find a baseline-manifest for the delta"); } } if( !noSign && !g.markPrivate && clearsign(&manifest, &manifest) ){ prompt_user("unable to sign manifest. continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' ){ fossil_exit(1); } } |
︙ | ︙ | |||
1796 1797 1798 1799 1800 1801 1802 | } nvid = content_put(&manifest); if( nvid==0 ){ fossil_fatal("trouble committing manifest: %s", g.zErrMsg); } db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); | | > > > | 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 | } nvid = content_put(&manifest); if( nvid==0 ){ fossil_fatal("trouble committing manifest: %s", g.zErrMsg); } db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); if( manifest_crosslink(nvid, &manifest, dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){ fossil_fatal("%s\n", g.zErrMsg); } assert( blob_is_reset(&manifest) ); content_deltify(vid, nvid, 0); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid" " WHERE id=-4"); while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
1837 1838 1839 1840 1841 1842 1843 | " WHERE is_selected(id);" , vid, nvid ); db_lset_int("checkout", nvid); if( useCksum ){ /* Verify that the repository checksum matches the expected checksum | | | 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 | " WHERE is_selected(id);" , vid, nvid ); db_lset_int("checkout", nvid); if( useCksum ){ /* Verify that the repository checksum matches the expected checksum ** calculated before the check-in started (and stored as the R record ** of the manifest file). */ vfile_aggregate_checksum_repository(nvid, &cksum2); if( blob_compare(&cksum1, &cksum2) ){ vfile_compare_repository_to_disk(nvid); fossil_fatal("working checkout does not match what would have ended " "up in the repository: %b versus %b", |
︙ | ︙ | |||
1881 1882 1883 1884 1885 1886 1887 | if( dryRunFlag ){ db_end_transaction(1); exit(1); } db_end_transaction(0); if( !g.markPrivate ){ | | | 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 | if( dryRunFlag ){ db_end_transaction(1); exit(1); } db_end_transaction(0); if( !g.markPrivate ){ autosync_loop(SYNC_PUSH|SYNC_PULL, db_get_int("autosync-tries", 1)); } if( count_nonbranch_children(vid)>1 ){ fossil_print("**** warning: a fork has occurred *****\n"); } } |
Changes to src/checkout.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 | /* ** Check to see if there is an existing checkout that has been ** modified. Return values: ** ** 0: There is an existing checkout but it is unmodified ** 1: There is a modified checkout - there are unsaved changes | < | < | | | > | | | | > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | /* ** Check to see if there is an existing checkout that has been ** modified. Return values: ** ** 0: There is an existing checkout but it is unmodified ** 1: There is a modified checkout - there are unsaved changes */ int unsaved_changes(unsigned int cksigFlags){ int vid; db_must_be_within_tree(); vid = db_lget_int("checkout",0); vfile_check_signature(vid, cksigFlags|CKSIG_ENOTFILE); return db_exists("SELECT 1 FROM vfile WHERE chnged" " OR coalesce(origname!=pathname,0)"); } /* ** Undo the current check-out. Unlink all files from the disk. ** Clear the VFILE table. */ void uncheckout(int vid){ if( vid>0 ){ vfile_unlink(vid); } db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid); } /* ** Given the abbreviated UUID name of a version, load the content of that ** version in the VFILE table. Return the VID for the version. ** ** If anything goes wrong, panic. */ int load_vfile(const char *zName, int forceMissingFlag){ Blob uuid; int vid; blob_init(&uuid, zName, -1); if( name_to_uuid(&uuid, 1, "ci") ){ fossil_fatal("%s", g.zErrMsg); } vid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid); if( vid==0 ){ fossil_fatal("no such check-in: %s", g.argv[2]); } if( !is_a_version(vid) ){ fossil_fatal("object [%S] is not a check-in", blob_str(&uuid)); } if( load_vfile_from_rid(vid) && !forceMissingFlag ){ fossil_fatal("missing content, unable to checkout"); }; return vid; } /* ** Set or clear the vfile.isexe flag for a file. */ static void set_or_clear_isexe(const char *zFilename, int vid, int onoff){ |
︙ | ︙ | |||
134 135 136 137 138 139 140 | Blob manifest; Blob hash; if( db_get_boolean("manifest",0) ){ blob_zero(&manifest); content_get(vid, &manifest); zManFile = mprintf("%smanifest", g.zLocalRoot); | < < > > > | | | | > > | > > > > > | | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | Blob manifest; Blob hash; if( db_get_boolean("manifest",0) ){ blob_zero(&manifest); content_get(vid, &manifest); zManFile = mprintf("%smanifest", g.zLocalRoot); blob_zero(&hash); sha1sum_blob(&manifest, &hash); sterilize_manifest(&manifest); blob_write_to_file(&manifest, zManFile); free(zManFile); zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); blob_append(&hash, "\n", 1); blob_write_to_file(&hash, zManFile); free(zManFile); blob_reset(&hash); }else{ if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){ zManFile = mprintf("%smanifest", g.zLocalRoot); file_delete(zManFile); free(zManFile); } if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){ zManFile = mprintf("%smanifest.uuid", g.zLocalRoot); file_delete(zManFile); free(zManFile); } } } /* ** COMMAND: checkout* ** COMMAND: co* ** ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS? ** or: %fossil co ?VERSION | --latest? ?OPTIONS? ** ** Check out a version specified on the command-line. This command ** will abort if there are edited files in the current checkout unless ** the --force option appears on the command-line. The --keep option ** leaves files on disk unchanged, except the manifest and manifest.uuid ** files. ** ** The --latest flag can be used in place of VERSION to checkout the ** latest version in the repository. ** ** Options: ** --force Ignore edited files in the current checkout ** --keep Only update the manifest and manifest.uuid files ** --force-missing Force checkout even if content is missing ** ** See also: update */ void checkout_cmd(void){ int forceFlag; /* Force checkout even if edits exist */ int forceMissingFlag; /* Force checkout even if missing content */ int keepFlag; /* Do not change any files on disk */ int latestFlag; /* Checkout the latest version */ char *zVers; /* Version to checkout */ int promptFlag; /* True to prompt before overwriting */ int vid, prior; Blob cksum1, cksum1b, cksum2; db_must_be_within_tree(); db_begin_transaction(); forceFlag = find_option("force","f",0)!=0; forceMissingFlag = find_option("force-missing",0,0)!=0; keepFlag = find_option("keep",0,0)!=0; latestFlag = find_option("latest",0,0)!=0; promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0; /* We should be done with options.. */ verify_all_options(); if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){ usage("VERSION|--latest ?--force? ?--keep?"); } if( !forceFlag && unsaved_changes(0) ){ fossil_fatal("there are unsaved changes in the current checkout"); } if( forceFlag ){ db_multi_exec("DELETE FROM vfile"); prior = 0; }else{ prior = db_lget_int("checkout",0); } if( latestFlag ){ compute_leaves(db_lget_int("checkout",0), 1); zVers = db_text(0, "SELECT uuid FROM leaves, event, blob" " WHERE event.objid=leaves.rid AND blob.rid=leaves.rid" " ORDER BY event.mtime DESC"); if( zVers==0 ){ zVers = db_text(0, "SELECT uuid FROM event, blob" " WHERE event.objid=blob.rid AND event.type='ci'" " ORDER BY event.mtime DESC"); } if( zVers==0 ){ return; } }else{ zVers = g.argv[2]; } vid = load_vfile(zVers, forceMissingFlag); if( prior==vid ){ return; } if( !keepFlag ){ uncheckout(prior); } db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); |
︙ | ︙ | |||
286 287 288 289 290 291 292 | ** --force|-f necessary to close a check out with uncommitted changes ** ** See also: open */ void close_cmd(void){ int forceFlag = find_option("force","f",0)!=0; db_must_be_within_tree(); | > > > > | < | | > > | 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | ** --force|-f necessary to close a check out with uncommitted changes ** ** See also: open */ void close_cmd(void){ int forceFlag = find_option("force","f",0)!=0; db_must_be_within_tree(); /* We should be done with options.. */ verify_all_options(); if( !forceFlag && unsaved_changes(0) ){ fossil_fatal("there are unsaved changes in the current checkout"); } if( !forceFlag && db_table_exists("localdb","stash") && db_exists("SELECT 1 FROM %s.stash", db_name("localdb")) ){ fossil_fatal("closing the checkout will delete your stash"); } if( db_is_writeable("repository") ){ char *zUnset = mprintf("ckout:%q", g.zLocalRoot); db_unset(zUnset, 1); fossil_free(zUnset); } unlink_local_database(1); db_close(1); unlink_local_database(0); } |
Changes to src/clone.c.
︙ | ︙ | |||
81 82 83 84 85 86 87 | /* ** COMMAND: clone ** ** Usage: %fossil clone ?OPTIONS? URL FILENAME ** ** Make a clone of a repository specified by URL in the local | | | > | > > > > < > > > | > > > > > > > > > | < < | > | | | | | > > > > | > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | /* ** COMMAND: clone ** ** Usage: %fossil clone ?OPTIONS? URL FILENAME ** ** Make a clone of a repository specified by URL in the local ** file named FILENAME. ** ** URL must be in one of the following form: ([...] mean optional) ** HTTP/HTTPS protocol: ** http[s]://[userid[:password]@]host[:port][/path] ** ** SSH protocol: ** ssh://[userid[:password]@]host[:port]/path/to/repo.fossil\\ ** [?fossil=path/to/fossil.exe] ** ** Filesystem: ** [file://]path/to/repo.fossil ** ** Note: For ssh and filesystem, path must have an extra leading ** '/' to use an absolute path. ** ** By default, your current login name is used to create the default ** admin user. This can be overridden using the -A|--admin-user ** parameter. ** ** Options: ** --admin-user|-A USERNAME Make USERNAME the administrator ** --once Don't save url. ** --private Also clone private branches ** --ssl-identity=filename Use the SSL identity if requested by the server ** --ssh-command|-c 'command' Use this SSH command ** --httpauth|-B 'user:pass' Add HTTP Basic Authorization to requests ** --verbose Show more statistics in output ** ** See also: init */ void clone_cmd(void){ char *zPassword; const char *zDefaultUser; /* Optional name of the default user */ const char *zHttpAuth; /* HTTP Authorization user:pass information */ int nErr = 0; int urlFlags = URL_PROMPT_PW | URL_REMEMBER; int syncFlags = SYNC_CLONE; /* Also clone private branches */ if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE; if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; if( find_option("verbose",0,0)!=0) syncFlags |= SYNC_VERBOSE; zHttpAuth = find_option("httpauth","B",1); zDefaultUser = find_option("admin-user","A",1); clone_ssh_find_options(); url_proxy_options(); /* We should be done with options.. */ verify_all_options(); if( g.argc < 4 ){ usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); } db_open_config(0); if( -1 != file_size(g.argv[3]) ){ fossil_fatal("file already exists: %s", g.argv[3]); } url_parse(g.argv[2], urlFlags); if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user; if( g.url.isFile ){ file_copy(g.url.name, g.argv[3]); db_close(1); db_open_repository(g.argv[3]); db_record_repository_filename(g.argv[3]); url_remember(); if( !(syncFlags & SYNC_PRIVATE) ) delete_private_content(); shun_artifacts(); db_create_default_users(1, zDefaultUser); if( zDefaultUser ){ g.zLogin = zDefaultUser; }else{ g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'"); } fossil_print("Repository cloned into %s\n", g.argv[3]); }else{ db_create_repository(g.argv[3]); db_open_repository(g.argv[3]); db_begin_transaction(); db_record_repository_filename(g.argv[3]); db_initial_setup(0, 0, zDefaultUser); user_select(); db_set("content-schema", CONTENT_SCHEMA, 0); db_set("aux-schema", AUX_SCHEMA_MAX, 0); db_set("rebuilt", get_version(), 0); remember_or_get_http_auth(zHttpAuth, urlFlags & URL_REMEMBER, g.argv[2]); url_remember(); if( g.zSSLIdentity!=0 ){ /* If the --ssl-identity option was specified, store it as a setting */ Blob fn; blob_zero(&fn); file_canonical_name(g.zSSLIdentity, &fn, 0); db_set("ssl-identity", blob_str(&fn), 0); blob_reset(&fn); } db_multi_exec( "REPLACE INTO config(name,value,mtime)" " VALUES('server-code', lower(hex(randomblob(20))), now());" "DELETE FROM config WHERE name='project-code';" ); url_enable_proxy(0); clone_ssh_db_set_options(); url_get_password_if_needed(); g.xlinkClusterOnly = 1; nErr = client_sync(syncFlags,CONFIGSET_ALL,0); g.xlinkClusterOnly = 0; verify_cancel(); db_end_transaction(0); db_close(1); if( nErr ){ file_delete(g.argv[3]); fossil_fatal("server returned an error - clone aborted"); } db_open_repository(g.argv[3]); } db_begin_transaction(); fossil_print("Rebuilding repository meta-data...\n"); rebuild_db(0, 1, 0); fossil_print("Extra delta compression... "); fflush(stdout); extra_deltification(); db_end_transaction(0); fossil_print("\nVacuuming the database... "); fflush(stdout); if( db_int(0, "PRAGMA page_count")>1000 && db_int(0, "PRAGMA page_size")<8192 ){ db_multi_exec("PRAGMA page_size=8192;"); } db_multi_exec("VACUUM"); fossil_print("\nproject-id: %s\n", db_get("project-code", 0)); fossil_print("server-id: %s\n", db_get("server-code", 0)); zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); } /* ** If user chooses to use HTTP Authentication over unencrypted HTTP, ** remember decision. Otherwise, if the URL is being changed and no ** preference has been indicated, err on the safe side and revert the ** decision. Set the global preference if the URL is not being changed. */ void remember_or_get_http_auth( const char *zHttpAuth, /* Credentials in the form "user:password" */ int fRemember, /* True to remember credentials for later reuse */ const char *zUrl /* URL for which these credentials apply */ ){ char *zKey = mprintf("http-auth:%s", g.url.canonical); if( zHttpAuth && zHttpAuth[0] ){ g.zHttpAuth = mprintf("%s", zHttpAuth); } if( fRemember ){ if( g.zHttpAuth && g.zHttpAuth[0] ){ set_httpauth(g.zHttpAuth); }else if( zUrl && zUrl[0] ){ db_unset(zKey, 0); }else{ g.zHttpAuth = get_httpauth(); } }else if( g.zHttpAuth==0 && zUrl==0 ){ g.zHttpAuth = get_httpauth(); } free(zKey); } /* ** Get the HTTP Authorization preference from db. */ char *get_httpauth(void){ char *zKey = mprintf("http-auth:%s", g.url.canonical); char * rc = unobscure(db_get(zKey, 0)); free(zKey); return rc; } /* ** Set the HTTP Authorization preference in db. */ void set_httpauth(const char *zHttpAuth){ char *zKey = mprintf("http-auth:%s", g.url.canonical); db_set(zKey, obscure(zHttpAuth), 0); free(zKey); } /* ** Look for SSH clone command line options and setup in globals. */ void clone_ssh_find_options(void){ const char *zSshCmd; /* SSH command string */ zSshCmd = find_option("ssh-command","c",1); if( zSshCmd && zSshCmd[0] ){ g.zSshCmd = mprintf("%s", zSshCmd); } } /* ** Set SSH options discovered in global variables (set from command line ** options). */ void clone_ssh_db_set_options(void){ if( g.zSshCmd && g.zSshCmd[0] ){ db_set("ssh-command", g.zSshCmd, 0); } } |
Added src/codecheck1.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This program reads Fossil source code files and tries to verify that ** printf-style format strings are correct. ** ** This program implements a compile-time validation step on the Fossil ** source code. Running this program is entirely optional. Its role is ** similar to the -Wall compiler switch on gcc, or the scan-build utility ** of clang, or other static analyzers. The purpose is to try to identify ** problems in the source code at compile-time. The difference is that this ** static checker is specifically designed for the particular printf formatter ** implementation used by Fossil. ** ** Checks include: ** ** * Verify that vararg formatting routines like blob_printf() or ** db_multi_exec() have the correct number of arguments for their ** format string. ** ** * For routines designed to generate SQL, warn about the use of %s ** which might allow SQL injection. */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <assert.h> /* ** Malloc, aborting if it fails. */ void *safe_malloc(int nByte){ void *x = malloc(nByte); if( x==0 ){ fprintf(stderr, "failed to allocate %d bytes\n", nByte); exit(1); } return x; } void *safe_realloc(void *pOld, int nByte){ void *x = realloc(pOld, nByte); if( x==0 ){ fprintf(stderr, "failed to allocate %d bytes\n", nByte); exit(1); } return x; } /* ** Read the entire content of the file named zFilename into memory obtained ** from malloc(). Add a zero-terminator to the end. ** Return a pointer to that memory. */ static char *read_file(const char *zFilename){ FILE *in; char *z; int nByte; int got; in = fopen(zFilename, "rb"); if( in==0 ){ return 0; } fseek(in, 0, SEEK_END); nByte = ftell(in); fseek(in, 0, SEEK_SET); z = safe_malloc( nByte+1 ); got = fread(z, 1, nByte, in); z[got] = 0; fclose(in); return z; } /* ** When parsing the input file, the following token types are recognized. */ #define TK_SPACE 1 /* Whitespace or comments */ #define TK_ID 2 /* An identifier */ #define TK_STR 3 /* A string literal in double-quotes */ #define TK_OTHER 4 /* Any other token */ #define TK_EOF 99 /* End of file */ /* ** Determine the length and type of the token beginning at z[0] */ static int token_length(const char *z, int *pType, int *pLN){ int i; if( z[0]==0 ){ *pType = TK_EOF; return 0; } if( z[0]=='"' || z[0]=='\'' ){ for(i=1; z[i] && z[i]!=z[0]; i++){ if( z[i]=='\\' && z[i+1]!=0 ){ if( z[i+1]=='\n' ) (*pLN)++; i++; } } if( z[i]!=0 ) i++; *pType = z[0]=='"' ? TK_STR : TK_OTHER; return i; } if( isalnum(z[0]) || z[0]=='_' ){ for(i=1; isalnum(z[i]) || z[i]=='_'; i++){} *pType = isalpha(z[0]) || z[0]=='_' ? TK_ID : TK_OTHER; return i; } if( isspace(z[0]) ){ if( z[0]=='\n' ) (*pLN)++; for(i=1; isspace(z[i]); i++){ if( z[i]=='\n' ) (*pLN)++; } *pType = TK_SPACE; return i; } if( z[0]=='/' && z[1]=='*' ){ for(i=2; z[i] && (z[i]!='*' || z[i+1]!='/'); i++){ if( z[i]=='\n' ) (*pLN)++; } if( z[i] ) i += 2; *pType = TK_SPACE; return i; } if( z[0]=='/' && z[1]=='/' ){ for(i=2; z[i] && z[i]!='\n'; i++){} if( z[i] ){ (*pLN)++; i++; } *pType = TK_SPACE; return i; } *pType = TK_OTHER; return 1; } /* ** Return the next non-whitespace token */ const char *next_non_whitespace(const char *z, int *pLen, int *pType){ int len; int eType; int ln = 0; while( (len = token_length(z, &eType, &ln))>0 && eType==TK_SPACE ){ z += len; } *pLen = len; *pType = eType; return z; } /* ** Return index into z[] for the first balanced TK_OTHER token with ** value cValue. */ static int distance_to(const char *z, char cVal){ int len; int dist = 0; int eType; int nNest = 0; int ln = 0; while( z[0] && (len = token_length(z, &eType, &ln))>0 ){ if( eType==TK_OTHER ){ if( z[0]==cVal && nNest==0 ){ break; }else if( z[0]=='(' ){ nNest++; }else if( z[0]==')' ){ nNest--; } } dist += len; z += len; } return dist; } /* ** Return the first non-whitespace characters in z[] */ static const char *skip_space(const char *z){ while( isspace(z[0]) ){ z++; } return z; } /* ** Return true if the input is a string literal. */ static int is_string_lit(const char *z){ int nu1, nu2; z = next_non_whitespace(z, &nu1, &nu2); return z[0]=='"'; } /* ** Return true if the input is an expression of string literals: ** ** EXPR ? "..." : "..." */ static int is_string_expr(const char *z){ int len = 0, eType; const char *zOrig = z; len = distance_to(z, '?'); if( z[len]==0 && skip_space(z)[0]=='(' ){ z = skip_space(z) + 1; len = distance_to(z, '?'); } z += len; if( z[0]=='?' ){ z++; z = next_non_whitespace(z, &len, &eType); if( eType==TK_STR ){ z += len; z = next_non_whitespace(z, &len, &eType); if( eType==TK_OTHER && z[0]==':' ){ z += len; z = next_non_whitespace(z, &len, &eType); if( eType==TK_STR ){ z += len; z = next_non_whitespace(z, &len, &eType); if( eType==TK_EOF ) return 1; if( eType==TK_OTHER && z[0]==')' && skip_space(zOrig)[0]=='(' ){ z += len; z = next_non_whitespace(z, &len, &eType); if( eType==TK_EOF ) return 1; } } } } } return 0; } /* ** A list of functions that return strings that are safe to insert into ** SQL using %s. */ static const char *azSafeFunc[] = { "filename_collation", "db_name", "timeline_utc", "leaf_is_closed_sql", "timeline_query_for_www", "timeline_query_for_tty", "blob_sql_text", "glob_expr", "fossil_all_reserved_names", "configure_inop_rhs", "db_setting_inop_rhs", }; /* ** Return true if the input is an argument that is safe to use with %s ** while building an SQL statement. */ static int is_s_safe(const char *z){ int len, eType; int i; /* A string literal is safe for use with %s */ if( is_string_lit(z) ) return 1; /* Certain functions are guaranteed to return a string that is safe ** for use with %s */ z = next_non_whitespace(z, &len, &eType); for(i=0; i<sizeof(azSafeFunc)/sizeof(azSafeFunc[0]); i++){ if( eType==TK_ID && strncmp(z, azSafeFunc[i], len)==0 && strlen(azSafeFunc[i])==len ){ return 1; } } /* Expressions of the form: EXPR ? "..." : "...." can count as ** a string literal. */ if( is_string_expr(z) ) return 1; /* If the "safe-for-%s" comment appears in the argument, then ** let it through */ if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1; return 0; } /* ** Processing flags */ #define FMT_NO_S 0x00001 /* Do not allow %s substitutions */ /* ** A list of internal Fossil interfaces that take a printf-style format ** string. */ struct { const char *zFName; /* Name of the function */ int iFmtArg; /* Index of format argument. Leftmost is 1. */ unsigned fmtFlags; /* Processing flags */ } aFmtFunc[] = { { "admin_log", 1, 0 }, { "blob_append_sql", 2, FMT_NO_S }, { "blob_appendf", 2, 0 }, { "cgi_panic", 1, 0 }, { "cgi_redirectf", 1, 0 }, { "db_blob", 2, FMT_NO_S }, { "db_double", 2, FMT_NO_S }, { "db_err", 1, 0 }, { "db_exists", 1, FMT_NO_S }, { "db_int", 2, FMT_NO_S }, { "db_int64", 2, FMT_NO_S }, { "db_multi_exec", 1, FMT_NO_S }, { "db_optional_sql", 2, FMT_NO_S }, { "db_prepare", 2, FMT_NO_S }, { "db_prepare_ignore_error", 2, FMT_NO_S }, { "db_static_prepare", 2, FMT_NO_S }, { "db_text", 2, FMT_NO_S }, { "form_begin", 2, 0 }, { "fossil_error", 2, 0 }, { "fossil_errorlog", 1, 0 }, { "fossil_fatal", 1, 0 }, { "fossil_fatal_recursive", 1, 0 }, { "fossil_panic", 1, 0 }, { "fossil_print", 1, 0 }, { "fossil_trace", 1, 0 }, { "fossil_warning", 1, 0 }, { "href", 1, 0 }, { "json_new_string_f", 1, 0 }, { "mprintf", 1, 0 }, { "socket_set_errmsg", 1, 0 }, { "ssl_set_errmsg", 1, 0 }, { "style_header", 1, 0 }, { "style_set_current_page", 1, 0 }, { "webpage_error", 1, 0 }, { "xhref", 2, 0 }, }; /* ** Determine if the indentifier zIdent of length nIndent is a Fossil ** internal interface that uses a printf-style argument. Return zero if not. ** Return the index of the format string if true with the left-most ** argument having an index of 1. */ static int isFormatFunc(const char *zIdent, int nIdent, unsigned *pFlags){ int upr, lwr; lwr = 0; upr = sizeof(aFmtFunc)/sizeof(aFmtFunc[0]) - 1; while( lwr<=upr ){ unsigned x = (lwr + upr)/2; int c = strncmp(zIdent, aFmtFunc[x].zFName, nIdent); if( c==0 ){ if( aFmtFunc[x].zFName[nIdent]==0 ){ *pFlags = aFmtFunc[x].fmtFlags; return aFmtFunc[x].iFmtArg; } c = -1; } if( c<0 ){ upr = x - 1; }else{ lwr = x + 1; } } *pFlags = 0; return 0; } /* ** Return the expected number of arguments for the format string. ** Return -1 if the value cannot be computed. ** ** For each argument less than nType, store the conversion character ** for that argument in cType[i]. */ static int formatArgCount(const char *z, int nType, char *cType){ int nArg = 0; int i, k; int len; int eType; int ln = 0; while( z[0] ){ len = token_length(z, &eType, &ln); if( eType==TK_STR ){ for(i=1; i<len-1; i++){ if( z[i]!='%' ) continue; if( z[i+1]=='%' ){ i++; continue; } for(k=i+1; k<len && !isalpha(z[k]); k++){ if( z[k]=='*' || z[k]=='#' ){ if( nArg<nType ) cType[nArg] = z[k]; nArg++; } } if( z[k]!='R' ){ if( nArg<nType ) cType[nArg] = z[k]; nArg++; } } } z += len; } return nArg; } /* ** The function call that begins at zFCall[0] (which is on line lnFCall of the ** original file) is a function that uses a printf-style format string ** on argument number fmtArg. It has processings flags fmtFlags. Do ** compile-time checking on this function, output any errors, and return ** the number of errors. */ static int checkFormatFunc( const char *zFilename, /* Name of the file being processed */ const char *zFCall, /* Pointer to start of function call */ int lnFCall, /* Line number that holds z[0] */ int fmtArg, /* Format string should be this argument */ int fmtFlags /* Extra processing flags */ ){ int szFName; int eToken; int ln = lnFCall; int len; const char *zStart; char *z; char *zCopy; int nArg = 0; const char **azArg = 0; int i, k; int nErr = 0; char *acType; szFName = token_length(zFCall, &eToken, &ln); zStart = next_non_whitespace(zFCall+szFName, &len, &eToken); assert( zStart[0]=='(' && len==1 ); len = distance_to(zStart+1, ')'); zCopy = safe_malloc( len + 1 ); memcpy(zCopy, zStart+1, len); zCopy[len] = 0; azArg = 0; nArg = 0; z = zCopy; while( z[0] ){ len = distance_to(z, ','); azArg = safe_realloc((char*)azArg, (sizeof(azArg[0])+1)*(nArg+1)); azArg[nArg++] = skip_space(z); if( z[len]==0 ) break; z[len] = 0; for(i=len-1; i>0 && isspace(z[i]); i--){ z[i] = 0; } z += len + 1; } acType = (char*)&azArg[nArg]; if( fmtArg>nArg ){ printf("%s:%d: too few arguments to %.*s()\n", zFilename, lnFCall, szFName, zFCall); nErr++; }else{ const char *zFmt = azArg[fmtArg-1]; const char *zOverride = strstr(zFmt, "/*works-like:"); if( zOverride ) zFmt = zOverride + sizeof("/*works-like:")-1; if( !is_string_lit(zFmt) ){ printf("%s:%d: %.*s() has non-constant format string\n", zFilename, lnFCall, szFName, zFCall); nErr++; }else if( (k = formatArgCount(zFmt, nArg, acType))>=0 && nArg!=fmtArg+k ){ printf("%s:%d: too %s arguments to %.*s() " "- got %d and expected %d\n", zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"), szFName, zFCall, nArg, fmtArg+k); nErr++; }else if( fmtFlags & FMT_NO_S ){ for(i=0; i<nArg && i<k; i++){ if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') && !is_s_safe(azArg[fmtArg+i]) ){ printf("%s:%d: Argument %d to %.*s() not safe for SQL\n", zFilename, lnFCall, i+fmtArg, szFName, zFCall); nErr++; } } } } if( nErr ){ for(i=0; i<nArg; i++){ printf(" arg[%d]: %s\n", i, azArg[i]); } } free((char*)azArg); free(zCopy); return nErr; } /* ** Do a design-rule check of format strings for the file named zName ** with content zContent. Write errors on standard output. Return ** the number of errors. */ static int scan_file(const char *zName, const char *zContent){ const char *z; int ln = 0; int szToken; int eToken; const char *zPrev; int ePrev; int szPrev; int lnPrev; int nCurly = 0; int x; unsigned fmtFlags = 0; int nErr = 0; if( zContent==0 ){ printf("cannot read file: %s\n", zName); return 1; } for(z=zContent; z[0]; z += szToken){ szToken = token_length(z, &eToken, &ln); if( eToken==TK_SPACE ) continue; if( eToken==TK_OTHER ){ if( z[0]=='{' ){ nCurly++; }else if( z[0]=='}' ){ nCurly--; }else if( nCurly>0 && z[0]=='(' && ePrev==TK_ID && (x = isFormatFunc(zPrev,szPrev,&fmtFlags))>0 ){ nErr += checkFormatFunc(zName, zPrev, lnPrev, x, fmtFlags); } } zPrev = z; ePrev = eToken; szPrev = szToken; lnPrev = ln; } return nErr; } /* ** Check for format-string design rule violations on all files listed ** on the command-line. */ int main(int argc, char **argv){ int i; int nErr = 0; for(i=1; i<argc; i++){ char *zFile = read_file(argv[i]); nErr += scan_file(argv[i], zFile); free(zFile); } return nErr; } |
Changes to src/comformat.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 | ** ** This file contains code used to format and print comments or other ** text on a TTY. */ #include "config.h" #include "comformat.h" #include <assert.h> /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < < | | > > > > | > | | > > > > > > > > > > > > > | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | ** ** This file contains code used to format and print comments or other ** text on a TTY. */ #include "config.h" #include "comformat.h" #include <assert.h> #ifdef _WIN32 # include <windows.h> #else # include <termios.h> # if defined(TIOCGWINSZ) # include <sys/ioctl.h> # endif #endif #if INTERFACE #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags. */ #define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */ #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */ #define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */ #define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */ #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */ #define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */ #endif /* ** This is the previous value used by most external callers when they ** needed to specify a default maximum line length to be used with the ** comment_print() function. */ #ifndef COMMENT_LEGACY_LINE_LENGTH # define COMMENT_LEGACY_LINE_LENGTH (78) #endif /* ** This is the number of spaces to print when a tab character is seen. */ #ifndef COMMENT_TAB_WIDTH # define COMMENT_TAB_WIDTH (8) #endif /* ** This function sets the maximum number of characters to print per line ** based on the detected terminal line width, if available; otherwise, it ** uses the legacy default terminal line width minus the amount to indent. ** ** Zero is returned to indicate any failure. One is returned to indicate ** the successful detection of the terminal line width. Negative one is ** returned to indicate the terminal line width is using the hard-coded ** legacy default value. */ static int comment_set_maxchars( int indent, int *pMaxChars ){ #if defined(_WIN32) CONSOLE_SCREEN_BUFFER_INFO csbi; memset(&csbi, 0, sizeof(CONSOLE_SCREEN_BUFFER_INFO)); if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){ *pMaxChars = csbi.srWindow.Right - csbi.srWindow.Left - indent; return 1; } return 0; #elif defined(TIOCGWINSZ) struct winsize w; memset(&w, 0, sizeof(struct winsize)); if( ioctl(0, TIOCGWINSZ, &w)!=-1 ){ *pMaxChars = w.ws_col - indent; return 1; } return 0; #else /* ** Fallback to using more-or-less the "legacy semantics" of hard-coding ** the maximum line length to a value reasonable for the vast majority ** of supported systems. */ *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent; return -1; #endif } /* ** This function checks the current line being printed against the original ** comment text. Upon matching, it emits a new line and updates the provided ** character and line counts, if applicable. */ static int comment_check_orig( const char *zOrigText, /* [in] Original comment text ONLY, may be NULL. */ const char *zLine, /* [in] The comment line to print. */ int *pCharCnt, /* [in/out] Pointer to the line character count. */ int *pLineCnt /* [in/out] Pointer to the total line count. */ ){ if( zOrigText && fossil_strcmp(zLine, zOrigText)==0 ){ fossil_print("\n"); if( pCharCnt ) *pCharCnt = 0; if( pLineCnt ) (*pLineCnt)++; return 1; } return 0; } /* ** This function scans the specified comment line starting just after the ** initial index and returns the index of the next spacing character -OR- ** zero if such a character cannot be found. For the purposes of this ** algorithm, the NUL character is treated the same as a spacing character. */ static int comment_next_space( const char *zLine, /* [in] The comment line being printed. */ int index /* [in] The current character index being handled. */ ){ int nextIndex = index + 1; for(;;){ char c = zLine[nextIndex]; if( c==0 || fossil_isspace(c) ){ return nextIndex; } nextIndex++; } return 0; /* NOT REACHED */ } /* ** This function is called when printing a logical comment line to perform ** the necessary indenting. */ static void comment_print_indent( const char *zLine, /* [in] The comment line being printed. */ int indent, /* [in] Number of spaces to indent, zero for none. */ int trimCrLf, /* [in] Non-zero to trim leading/trailing CR/LF. */ int trimSpace, /* [in] Non-zero to trim leading/trailing spaces. */ int *piIndex /* [in/out] Pointer to first non-space character. */ ){ if( indent>0 ){ fossil_print("%*s", indent, ""); } if( zLine && piIndex ){ int index = *piIndex; if( trimCrLf ){ while( zLine[index]=='\r' || zLine[index]=='\n' ){ index++; } } if( trimSpace ){ while( fossil_isspace(zLine[index]) ){ index++; } } *piIndex = index; } } /* ** This function prints one logical line of a comment, stopping when it hits ** a new line -OR- runs out of space on the logical line. */ static void comment_print_line( const char *zOrigText, /* [in] Original comment text ONLY, may be NULL. */ const char *zLine, /* [in] The comment line to print. */ int origIndent, /* [in] Number of spaces to indent before the original ** comment. */ int indent, /* [in] Number of spaces to indent, before the line ** to print. */ int lineChars, /* [in] Maximum number of characters to print. */ int trimCrLf, /* [in] Non-zero to trim leading/trailing CR/LF. */ int trimSpace, /* [in] Non-zero to trim leading/trailing spaces. */ int wordBreak, /* [in] Non-zero to try breaking on word boundaries. */ int origBreak, /* [in] Non-zero to break before original comment. */ int *pLineCnt, /* [in/out] Pointer to the total line count. */ const char **pzLine /* [out] Pointer to the end of the logical line. */ ){ int index = 0, charCnt = 0, lineCnt = 0, maxChars; if( !zLine ) return; if( lineChars<=0 ) return; comment_print_indent(zLine, indent, trimCrLf, trimSpace, &index); maxChars = lineChars; for(;;){ int useChars = 1; char c = zLine[index]; if( c==0 ){ break; }else{ if( origBreak && index>0 ){ const char *zCurrent = &zLine[index]; if( comment_check_orig(zOrigText, zCurrent, &charCnt, &lineCnt) ){ comment_print_indent(zCurrent, origIndent, trimCrLf, trimSpace, &index); maxChars = lineChars; } } index++; } if( c=='\n' ){ lineCnt++; charCnt = 0; useChars = 0; }else if( c=='\t' ){ int nextIndex = comment_next_space(zLine, index); if( nextIndex<=0 || (nextIndex-index)>maxChars ){ break; } charCnt++; useChars = COMMENT_TAB_WIDTH; if( maxChars<useChars ){ fossil_print(" "); break; } }else if( wordBreak && fossil_isspace(c) ){ int nextIndex = comment_next_space(zLine, index); if( nextIndex<=0 || (nextIndex-index)>maxChars ){ break; } charCnt++; }else{ charCnt++; } assert( c!='\n' || charCnt==0 ); fossil_print("%c", c); maxChars -= useChars; if( maxChars==0 ) break; assert( maxChars>0 ); if( c=='\n' ) break; } if( charCnt>0 ){ fossil_print("\n"); lineCnt++; } if( pLineCnt ){ *pLineCnt += lineCnt; } if( pzLine ){ *pzLine = zLine + index; } } /* ** This is the legacy comment printing algorithm. It is being retained ** for backward compatibility. ** ** Given a comment string, format that string for printing on a TTY. ** Assume that the output cursors is indent spaces from the left margin ** and that a single line can contain no more than 'width' characters. ** Indent all subsequent lines by 'indent'. ** ** Returns the number of new lines emitted. */ static int comment_print_legacy( const char *zText, /* The comment text to be printed. */ int indent, /* Number of spaces to indent each non-initial line. */ int width /* Maximum number of characters per line. */ ){ int maxChars = width - indent; int si, sk, i, k; int doIndent = 0; char *zBuf; char zBuffer[400]; int lineCnt = 0; if( width<0 ){ comment_set_maxchars(indent, &maxChars); } if( zText==0 ) zText = "(NULL)"; if( maxChars<=0 ){ maxChars = strlen(zText); } if( maxChars >= (sizeof(zBuffer)) ){ zBuf = fossil_malloc(maxChars+1); }else{ zBuf = zBuffer; } for(;;){ while( fossil_isspace(zText[0]) ){ zText++; } if( zText[0]==0 ){ if( doIndent==0 ){ fossil_print("\n"); lineCnt = 1; } if( zBuf!=zBuffer) fossil_free(zBuf); return lineCnt; } for(sk=si=i=k=0; zText[i] && k<maxChars; i++){ char c = zText[i]; if( fossil_isspace(c) ){ si = i; sk = k; if( k==0 || zBuf[k-1]!=' ' ){ zBuf[k++] = ' '; } |
︙ | ︙ | |||
67 68 69 70 71 72 73 | } if( doIndent ){ fossil_print("%*s", indent, ""); } doIndent = 1; if( sk>0 && zText[i] ){ zText += si; | < < < > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > | > > > | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 | } if( doIndent ){ fossil_print("%*s", indent, ""); } doIndent = 1; if( sk>0 && zText[i] ){ zText += si; zBuf[sk] = 0; }else{ zText += i; zBuf[k] = 0; } fossil_print("%s\n", zBuf); lineCnt++; } } /* ** This is the comment printing function. The comment printing algorithm ** contained within it attempts to preserve the formatting present within ** the comment string itself while honoring line width limitations. There ** are several flags that modify the default behavior of this function: ** ** COMMENT_PRINT_LEGACY: Forces use of the legacy comment printing ** algorithm. For backward compatibility, ** this is the default. ** ** COMMENT_PRINT_TRIM_CRLF: Trims leading and trailing carriage-returns ** and line-feeds where they do not materially ** impact pre-existing formatting (i.e. at the ** start of the comment string -AND- right ** before line indentation). This flag does ** not apply to the legacy comment printing ** algorithm. This flag may be combined with ** COMMENT_PRINT_TRIM_SPACE. ** ** COMMENT_PRINT_TRIM_SPACE: Trims leading and trailing spaces where they ** do not materially impact the pre-existing ** formatting (i.e. at the start of the comment ** string -AND- right before line indentation). ** This flag does not apply to the legacy ** comment printing algorithm. This flag may ** be combined with COMMENT_PRINT_TRIM_CRLF. ** ** COMMENT_PRINT_WORD_BREAK: Attempts to break lines on word boundaries ** while honoring the logical line length. ** If this flag is not specified, honoring the ** logical line length may result in breaking ** lines in the middle of words. This flag ** does not apply to the legacy comment ** printing algorithm. ** ** COMMENT_PRINT_ORIG_BREAK: Looks for the original comment text within ** the text being printed. Upon matching, a ** new line will be emitted, thus preserving ** more of the pre-existing formatting. ** ** Given a comment string, format that string for printing on a TTY. ** Assume that the output cursors is indent spaces from the left margin ** and that a single line can contain no more than 'width' characters. ** Indent all subsequent lines by 'indent'. ** ** Returns the number of new lines emitted. */ int comment_print( const char *zText, /* The comment text to be printed. */ const char *zOrigText, /* Original comment text ONLY, may be NULL. */ int indent, /* Spaces to indent each non-initial line. */ int width, /* Maximum number of characters per line. */ int flags /* Zero or more "COMMENT_PRINT_*" flags. */ ){ int maxChars = width - indent; int legacy = flags & COMMENT_PRINT_LEGACY; int trimCrLf = flags & COMMENT_PRINT_TRIM_CRLF; int trimSpace = flags & COMMENT_PRINT_TRIM_SPACE; int wordBreak = flags & COMMENT_PRINT_WORD_BREAK; int origBreak = flags & COMMENT_PRINT_ORIG_BREAK; int lineCnt = 0; const char *zLine; if( legacy ){ return comment_print_legacy(zText, indent, width); } if( width<0 ){ comment_set_maxchars(indent, &maxChars); } if( zText==0 ) zText = "(NULL)"; if( maxChars<=0 ){ maxChars = strlen(zText); } if( trimSpace ){ while( fossil_isspace(zText[0]) ){ zText++; } } if( zText[0]==0 ){ fossil_print("\n"); lineCnt++; return lineCnt; } zLine = zText; for(;;){ comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0, maxChars, trimCrLf, trimSpace, wordBreak, origBreak, &lineCnt, &zLine); if( !zLine || !zLine[0] ) break; } return lineCnt; } /* ** ** COMMAND: test-comment-format ** ** Usage: %fossil test-comment-format ?OPTIONS? PREFIX TEXT ?ORIGTEXT? ** ** Test comment formatting and printing. Use for testing only. ** ** Options: ** --file The comment text is really just a file name to ** read it from. ** --decode Decode the text using the same method used when ** handling the value of a C-card from a manifest. ** --legacy Use the legacy comment printing algorithm. ** --trimcrlf Enable trimming of leading/trailing CR/LF. ** --trimspace Enable trimming of leading/trailing spaces. ** --wordbreak Attempt to break lines on word boundaries. ** --origbreak Attempt to break when the original comment text ** is detected. ** --indent Number of spaces to indent (default (-1) is to ** auto-detect). Zero means no indent. ** -W|--width <num> Width of lines (default (-1) is to auto-detect). ** Zero means no limit. */ void test_comment_format(void){ const char *zWidth; const char *zIndent; const char *zPrefix; char *zText; char *zOrigText; int indent, width; int fromFile = find_option("file", 0, 0)!=0; int decode = find_option("decode", 0, 0)!=0; int flags = COMMENT_PRINT_NONE; if( find_option("legacy", 0, 0) ){ flags |= COMMENT_PRINT_LEGACY; } if( find_option("trimcrlf", 0, 0) ){ flags |= COMMENT_PRINT_TRIM_CRLF; } if( find_option("trimspace", 0, 0) ){ flags |= COMMENT_PRINT_TRIM_SPACE; } if( find_option("wordbreak", 0, 0) ){ flags |= COMMENT_PRINT_WORD_BREAK; } if( find_option("origbreak", 0, 0) ){ flags |= COMMENT_PRINT_ORIG_BREAK; } zWidth = find_option("width","W",1); if( zWidth ){ width = atoi(zWidth); }else{ width = -1; /* automatic */ } zIndent = find_option("indent",0,1); if( zIndent ){ indent = atoi(zIndent); }else{ indent = -1; /* automatic */ } if( g.argc!=4 && g.argc!=5 ){ usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?"); } zPrefix = g.argv[2]; zText = g.argv[3]; if( g.argc==5 ){ zOrigText = g.argv[4]; }else{ zOrigText = 0; } if( fromFile ){ Blob fileData; blob_read_from_file(&fileData, zText); zText = mprintf("%s", blob_str(&fileData)); blob_reset(&fileData); if( zOrigText ){ blob_read_from_file(&fileData, zOrigText); zOrigText = mprintf("%s", blob_str(&fileData)); blob_reset(&fileData); } } if( decode ){ zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText); defossilize(zText); if( zOrigText ){ zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText); defossilize(zOrigText); } } if( indent<0 ){ indent = strlen(zPrefix); } if( zPrefix && *zPrefix ){ fossil_print("%s", zPrefix); } fossil_print("(%d lines output)\n", comment_print(zText, zOrigText, indent, width, flags)); if( zOrigText && zOrigText!=g.argv[4] ) fossil_free(zOrigText); if( zText && zText!=g.argv[3] ) fossil_free(zText); } |
Changes to src/config.h.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** some linux distributions, and possibly other unixes as well. */ #define _LARGE_FILE 1 #ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 #endif #define _LARGEFILE_SOURCE 1 #ifdef HAVE_AUTOCONFIG_H #include "autoconfig.h" #endif #ifndef _RC_COMPILE_ | > > > > > > > > > > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | ** some linux distributions, and possibly other unixes as well. */ #define _LARGE_FILE 1 #ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 #endif #define _LARGEFILE_SOURCE 1 /* Needed for various definitions... */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif /* Make sure that in Win32 MinGW builds, _USE_32BIT_TIME_T is always defined. */ #if defined(_WIN32) && !defined(_WIN64) && !defined(_MSC_VER) && !defined(_USE_32BIT_TIME_T) # define _USE_32BIT_TIME_T #endif #ifdef HAVE_AUTOCONFIG_H #include "autoconfig.h" #endif #ifndef _RC_COMPILE_ |
︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 66 | # endif #else # include <sys/types.h> # include <signal.h> # include <pwd.h> #endif /* ** Define the compiler variant, used to compile the project */ #if !defined(COMPILER_NAME) # if defined(__DMC__) | > > > > > > > > > > > | > > > > | > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | > | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | # endif #else # include <sys/types.h> # include <signal.h> # include <pwd.h> #endif /* ** Utility macro to wrap an argument with double quotes. */ #if !defined(COMPILER_STRINGIFY) # define COMPILER_STRINGIFY(x) COMPILER_STRINGIFY1(x) # define COMPILER_STRINGIFY1(x) #x #endif /* ** Define the compiler variant, used to compile the project */ #if !defined(COMPILER_NAME) # if defined(__DMC__) # if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION) # define COMPILER_NAME "dmc-" COMPILER_VERSION # else # define COMPILER_NAME "dmc" # endif # elif defined(__POCC__) # if defined(_M_X64) # if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION) # define COMPILER_NAME "pellesc64-" COMPILER_VERSION # else # define COMPILER_NAME "pellesc64" # endif # else # if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION) # define COMPILER_NAME "pellesc32-" COMPILER_VERSION # else # define COMPILER_NAME "pellesc32" # endif # endif # elif defined(_MSC_VER) # if !defined(COMPILER_VERSION) # define COMPILER_VERSION COMPILER_STRINGIFY(_MSC_VER) # endif # if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION) # define COMPILER_NAME "msc-" COMPILER_VERSION # else # define COMPILER_NAME "msc" # endif # elif defined(__MINGW32__) # if !defined(COMPILER_VERSION) # if defined(__MINGW_VERSION) # if defined(__GNUC__) # if defined(__VERSION__) # define COMPILER_VERSION COMPILER_STRINGIFY(__MINGW_VERSION) "-gcc-" __VERSION__ # else # define COMPILER_VERSION COMPILER_STRINGIFY(__MINGW_VERSION) "-gcc" # endif # else # define COMPILER_VERSION COMPILER_STRINGIFY(__MINGW_VERSION) # endif # elif defined(__MINGW32_VERSION) # if defined(__GNUC__) # if defined(__VERSION__) # define COMPILER_VERSION COMPILER_STRINGIFY(__MINGW32_VERSION) "-gcc-" __VERSION__ # else # define COMPILER_VERSION COMPILER_STRINGIFY(__MINGW32_VERSION) "-gcc" # endif # else # define COMPILER_VERSION COMPILER_STRINGIFY(__MINGW32_VERSION) # endif # endif # endif # if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION) # define COMPILER_NAME "mingw32-" COMPILER_VERSION # else # define COMPILER_NAME "mingw32" # endif # elif defined(_WIN32) # define COMPILER_NAME "win32" # elif defined(__GNUC__) # if !defined(COMPILER_VERSION) # if defined(__VERSION__) # define COMPILER_VERSION __VERSION__ # endif # endif # if defined(COMPILER_VERSION) && !defined(NO_COMPILER_VERSION) # define COMPILER_NAME "gcc-" COMPILER_VERSION # else # define COMPILER_NAME "gcc" # endif # else # define COMPILER_NAME "unknown" # endif #endif #if !defined(_RC_COMPILE_) && !defined(SQLITE_AMALGAMATION) #include "sqlite3.h" /* ** On Solaris, getpass() will only return up to 8 characters. getpassphrase() returns up to 257. */ #if HAVE_GETPASSPHRASE |
︙ | ︙ | |||
116 117 118 119 120 121 122 | /* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. ** | | | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | /* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. ** ** The correct "ANSI" way to do this is to use the intptr_t type. ** Unfortunately, that typedef is not available on all compilers, or ** if it is available, it requires an #include of specific headers ** that vary from one machine to the next. */ #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ # define FOSSIL_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define FOSSIL_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) |
︙ | ︙ |
Changes to src/configure.c.
︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #define CONFIGSET_XFER 0x000080 /* Transfer configuration */ #define CONFIGSET_ALL 0x0000ff /* Everything */ #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ #define CONFIGSET_OLDFORMAT 0x200000 /* Use the legacy format */ #endif /* INTERFACE */ /* ** Names of the configuration sets */ static struct { const char *zName; /* Name of the configuration set */ | > > > > > > > | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #define CONFIGSET_XFER 0x000080 /* Transfer configuration */ #define CONFIGSET_ALL 0x0000ff /* Everything */ #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ #define CONFIGSET_OLDFORMAT 0x200000 /* Use the legacy format */ /* ** This mask is used for the common TH1 configuration settings (i.e. those ** that are not specific to one particular subsystem, such as the transfer ** subsystem). */ #define CONFIGSET_TH1 (CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER) #endif /* INTERFACE */ /* ** Names of the configuration sets */ static struct { const char *zName; /* Name of the configuration set */ |
︙ | ︙ | |||
77 78 79 80 81 82 83 84 85 86 87 | static struct { const char *zName; /* Name of the configuration parameter */ int groupMask; /* Which config groups is it part of */ } aConfig[] = { { "css", CONFIGSET_CSS }, { "header", CONFIGSET_SKIN }, { "footer", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "background-mimetype", CONFIGSET_SKIN }, { "background-image", CONFIGSET_SKIN }, | > < > > > > > > > | > | | > > > | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | static struct { const char *zName; /* Name of the configuration parameter */ int groupMask; /* Which config groups is it part of */ } aConfig[] = { { "css", CONFIGSET_CSS }, { "header", CONFIGSET_SKIN }, { "footer", CONFIGSET_SKIN }, { "details", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "background-mimetype", CONFIGSET_SKIN }, { "background-image", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "timeline-plaintext", CONFIGSET_SKIN }, { "adunit", CONFIGSET_SKIN }, { "adunit-omit-if-admin", CONFIGSET_SKIN }, { "adunit-omit-if-user", CONFIGSET_SKIN }, #ifdef FOSSIL_ENABLE_TH1_DOCS { "th1-docs", CONFIGSET_TH1 }, #endif #ifdef FOSSIL_ENABLE_TH1_HOOKS { "th1-hooks", CONFIGSET_TH1 }, #endif { "th1-setup", CONFIGSET_TH1 }, { "th1-uri-regexp", CONFIGSET_TH1 }, #ifdef FOSSIL_ENABLE_TCL { "tcl", CONFIGSET_TH1 }, { "tcl-setup", CONFIGSET_TH1 }, #endif { "project-name", CONFIGSET_PROJ }, { "short-project-name", CONFIGSET_PROJ }, { "project-description", CONFIGSET_PROJ }, { "index-page", CONFIGSET_PROJ }, { "manifest", CONFIGSET_PROJ }, { "binary-glob", CONFIGSET_PROJ }, { "clean-glob", CONFIGSET_PROJ }, { "ignore-glob", CONFIGSET_PROJ }, { "keep-glob", CONFIGSET_PROJ }, { "crnl-glob", CONFIGSET_PROJ }, { "encoding-glob", CONFIGSET_PROJ }, { "empty-dirs", CONFIGSET_PROJ }, { "allow-symlinks", CONFIGSET_PROJ }, { "dotfiles", CONFIGSET_PROJ }, { "ticket-table", CONFIGSET_TKT }, { "ticket-common", CONFIGSET_TKT }, { "ticket-change", CONFIGSET_TKT }, { "ticket-newpage", CONFIGSET_TKT }, { "ticket-viewpage", CONFIGSET_TKT }, { "ticket-editpage", CONFIGSET_TKT }, |
︙ | ︙ | |||
128 129 130 131 132 133 134 135 136 137 138 139 140 141 | { "@concealed", CONFIGSET_ADDR }, { "@shun", CONFIGSET_SHUN }, { "xfer-common-script", CONFIGSET_XFER }, { "xfer-push-script", CONFIGSET_XFER }, }; static int iConfig = 0; /* ** Return name of first configuration property matching the given mask. */ | > > | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | { "@concealed", CONFIGSET_ADDR }, { "@shun", CONFIGSET_SHUN }, { "xfer-common-script", CONFIGSET_XFER }, { "xfer-push-script", CONFIGSET_XFER }, { "xfer-commit-script", CONFIGSET_XFER }, { "xfer-ticket-script", CONFIGSET_XFER }, }; static int iConfig = 0; /* ** Return name of first configuration property matching the given mask. */ |
︙ | ︙ | |||
175 176 177 178 179 180 181 | */ const char *configure_inop_rhs(int iMask){ Blob x; int i; const char *zSep = ""; blob_zero(&x); | | | | | | | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | */ const char *configure_inop_rhs(int iMask){ Blob x; int i; const char *zSep = ""; blob_zero(&x); blob_append_sql(&x, "("); for(i=0; i<count(aConfig); i++){ if( (aConfig[i].groupMask & iMask)==0 ) continue; if( aConfig[i].zName[0]=='@' ) continue; blob_append_sql(&x, "%s'%q'", zSep/*safe-for-%s*/, aConfig[i].zName); zSep = ","; } blob_append_sql(&x, ")"); return blob_sql_text(&x); } /* ** Return the mask for the named configuration parameter if it can be ** safely exported. Return 0 if the parameter is not safe to export. ** ** "Safe" in the previous paragraph means the permission is created to ** export the property. In other words, the requesting side has presented ** login credentials and has sufficient capabilities to access the requested ** information. */ int configure_is_exportable(const char *zName){ int i; int n = strlen(zName); if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){ zName++; n -= 2; } for(i=0; i<count(aConfig); i++){ if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){ int m = aConfig[i].groupMask; if( !g.perm.Admin ){ m &= ~CONFIGSET_USER; } if( !g.perm.RdAddr ){ m &= ~CONFIGSET_ADDR; } |
︙ | ︙ | |||
342 343 344 345 346 347 348 | @ photo BLOB -- JPEG image of this user @ ); @ INSERT INTO _xfer_reportfmt @ SELECT rn,owner,title,cols,sqlcode FROM reportfmt; @ INSERT INTO _xfer_user @ SELECT uid,login,pw,cap,cookie,ipaddr,cexpire,info,photo FROM user; ; | > | | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | @ photo BLOB -- JPEG image of this user @ ); @ INSERT INTO _xfer_reportfmt @ SELECT rn,owner,title,cols,sqlcode FROM reportfmt; @ INSERT INTO _xfer_user @ SELECT uid,login,pw,cap,cookie,ipaddr,cexpire,info,photo FROM user; ; assert( strchr(zSQL1,'%')==0 ); db_multi_exec(zSQL1 /*works-like:""*/); /* When the replace flag is set, add triggers that run the first time ** that new data is seen. The triggers run only once and delete all the ** existing data. */ if( replaceFlag ){ static const char zSQL2[] = |
︙ | ︙ | |||
371 372 373 374 375 376 377 | @ END; ; sqlite3_create_function(g.db, "config_is_reset", 1, SQLITE_UTF8, 0, config_is_reset_function, 0, 0); sqlite3_create_function(g.db, "config_reset", 1, SQLITE_UTF8, 0, config_reset_function, 0, 0); configHasBeenReset = 0; | > | > | > > > > > > > > > > > > > > > | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | @ END; ; sqlite3_create_function(g.db, "config_is_reset", 1, SQLITE_UTF8, 0, config_is_reset_function, 0, 0); sqlite3_create_function(g.db, "config_reset", 1, SQLITE_UTF8, 0, config_reset_function, 0, 0); configHasBeenReset = 0; assert( strchr(zSQL2,'%')==0 ); db_multi_exec(zSQL2 /*works-like:""*/); } } /* ** After receiving configuration data, call this routine to transfer ** the results into the main database. */ void configure_finalize_receive(void){ static const char zSQL[] = @ DELETE FROM user; @ INSERT INTO user SELECT * FROM _xfer_user; @ DELETE FROM reportfmt; @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt; @ DROP TABLE _xfer_user; @ DROP TABLE _xfer_reportfmt; ; assert( strchr(zSQL,'%')==0 ); db_multi_exec(zSQL /*works-like:""*/); } /* ** Mask of modified configuration sets */ static int rebuildMask = 0; /* ** Rebuild auxiliary tables as required by configuration changes. */ void configure_rebuild(void){ if( rebuildMask & CONFIGSET_TKT ){ ticket_rebuild(); } rebuildMask = 0; } /* ** Return true if z[] is not a "safe" SQL token. A safe token is one of: ** ** * A string literal ** * A blob literal |
︙ | ︙ | |||
529 530 531 532 533 534 535 | thisMask = configure_is_exportable(aType[ii].zName); } if( (thisMask & groupMask)==0 ) return; blob_zero(&sql); if( groupMask & CONFIGSET_OVERWRITE ){ if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){ | | | | | | | > | | > | > | | | > | > | | 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 | thisMask = configure_is_exportable(aType[ii].zName); } if( (thisMask & groupMask)==0 ) return; blob_zero(&sql); if( groupMask & CONFIGSET_OVERWRITE ){ if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){ db_multi_exec("DELETE FROM \"%w\"", &aType[ii].zName[1]); configHasBeenReset |= thisMask; } blob_append_sql(&sql, "REPLACE INTO "); }else{ blob_append_sql(&sql, "INSERT OR IGNORE INTO "); } blob_append_sql(&sql, "\"%w\"(\"%w\", mtime", &zName[1], aType[ii].zPrimKey); for(jj=2; jj<nToken; jj+=2){ blob_append_sql(&sql, ",\"%w\"", azToken[jj]); } blob_append_sql(&sql,") VALUES(%s,%s", azToken[1] /*safe-for-%s*/, azToken[0] /*safe-for-%s*/); for(jj=2; jj<nToken; jj+=2){ blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/); } db_multi_exec("%s)", blob_sql_text(&sql)); if( db_changes()==0 ){ blob_reset(&sql); blob_append_sql(&sql, "UPDATE \"%w\" SET mtime=%s", &zName[1], azToken[0]/*safe-for-%s*/); for(jj=2; jj<nToken; jj+=2){ blob_append_sql(&sql, ", \"%w\"=%s", azToken[jj], azToken[jj+1]/*safe-for-%s*/); } blob_append_sql(&sql, " WHERE \"%w\"=%s AND mtime<%s", aType[ii].zPrimKey, azToken[1]/*safe-for-%s*/, azToken[0]/*safe-for-%s*/); db_multi_exec("%s", blob_sql_text(&sql)); } blob_reset(&sql); rebuildMask |= thisMask; }else{ /* Otherwise, the old format */ if( (configure_is_exportable(zName) & groupMask)==0 ) return; if( fossil_strcmp(zName, "logo-image")==0 ){ Stmt ins; db_prepare(&ins, "REPLACE INTO config(name, value, mtime) VALUES(:name, :value, now())" ); db_bind_text(&ins, ":name", zName); db_bind_blob(&ins, ":value", pContent); db_step(&ins); db_finalize(&ins); }else if( zName[0]=='@' ){ /* Notice that we are evaluating arbitrary SQL received from the ** client. But this can only happen if the client has authenticated ** as an administrator, so presumably we trust the client at this ** point. */ db_multi_exec("%s", blob_str(pContent) /*safe-for-%s*/); }else{ db_multi_exec( "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())", zName, blob_str(pContent) ); } } |
︙ | ︙ | |||
814 815 816 817 818 819 820 | ** ** %fossil configuration push AREA ?URL? ** ** Push the local configuration into the remote server identified ** by URL. Admin privilege is required on the remote server for ** this to work. When the same record exists both locally and on ** the remote end, the one that was most recently changed wins. | | | 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 | ** ** %fossil configuration push AREA ?URL? ** ** Push the local configuration into the remote server identified ** by URL. Admin privilege is required on the remote server for ** this to work. When the same record exists both locally and on ** the remote end, the one that was most recently changed wins. ** Use the --legacy flag when talking to older servers. ** ** %fossil configuration reset AREA ** ** Restore the configuration to the default. AREA as above. ** ** %fossil configuration sync AREA ?URL? ** |
︙ | ︙ | |||
895 896 897 898 899 900 901 | usage(mprintf("%s AREA ?URL?", zMethod)); } mask = configure_name_to_mask(g.argv[3], 1); if( g.argc==5 ){ zServer = g.argv[4]; } url_parse(zServer, URL_PROMPT_PW); | | | 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | usage(mprintf("%s AREA ?URL?", zMethod)); } mask = configure_name_to_mask(g.argv[3], 1); if( g.argc==5 ){ zServer = g.argv[4]; } url_parse(zServer, URL_PROMPT_PW); if( g.url.protocol==0 ) fossil_fatal("no server URL specified"); user_select(); url_enable_proxy("via proxy: "); if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT; if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; if( strncmp(zMethod, "push", n)==0 ){ client_sync(0,0,(unsigned)mask); }else if( strncmp(zMethod, "pull", n)==0 ){ |
︙ | ︙ | |||
931 932 933 934 935 936 937 | db_create_default_users(0, 0); }else if( fossil_strcmp(zName,"@concealed")==0 ){ db_multi_exec("DELETE FROM concealed"); }else if( fossil_strcmp(zName,"@shun")==0 ){ db_multi_exec("DELETE FROM shun"); }else if( fossil_strcmp(zName,"@reportfmt")==0 ){ db_multi_exec("DELETE FROM reportfmt"); | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 | db_create_default_users(0, 0); }else if( fossil_strcmp(zName,"@concealed")==0 ){ db_multi_exec("DELETE FROM concealed"); }else if( fossil_strcmp(zName,"@shun")==0 ){ db_multi_exec("DELETE FROM shun"); }else if( fossil_strcmp(zName,"@reportfmt")==0 ){ db_multi_exec("DELETE FROM reportfmt"); assert( strchr(zRepositorySchemaDefaultReports,'%')==0 ); db_multi_exec(zRepositorySchemaDefaultReports /*works-like:""*/); } } db_end_transaction(0); fossil_print("Configuration reset to factory defaults.\n"); fossil_print("To recover, use: %s %s import %s\n", g.argv[0], g.argv[1], zBackup); rebuildMask |= mask; }else { fossil_fatal("METHOD should be one of:" " export import merge pull push reset"); } configure_rebuild(); } /* ** COMMAND: test-var-list ** ** Usage: %fossil test-var-list ?PATTERN? ?--unset? ?--mtime? ** ** Show the content of the CONFIG table in a repository. If PATTERN is ** specified, then only show the entries that match that glob pattern. ** Last modification time is shown if the --mtime option is present. ** ** If the --unset option is included, then entries are deleted rather than ** being displayed. WARNING! This cannot be undone. Be sure you know what ** you are doing! The --unset option only works if there is a PATTERN. ** Probably you should run the command once without --unset to make sure ** you know exactly what is being deleted. ** ** If not in an open check-out, use the -R REPO option to specify a ** a repository. */ void test_var_list_cmd(void){ Stmt q; int i, j; const char *zPattern = 0; int doUnset; int showMtime; Blob sql; Blob ans; unsigned char zTrans[1000]; doUnset = find_option("unset",0,0)!=0; showMtime = find_option("mtime",0,0)!=0; db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); verify_all_options(); if( g.argc>=3 ){ zPattern = g.argv[2]; } blob_init(&sql,0,0); blob_appendf(&sql, "SELECT name, value, datetime(mtime,'unixepoch')" " FROM config"); if( zPattern ){ blob_appendf(&sql, " WHERE name GLOB %Q", zPattern); } if( showMtime ){ blob_appendf(&sql, " ORDER BY mtime, name"); }else{ blob_appendf(&sql, " ORDER BY name"); } db_prepare(&q, "%s", blob_str(&sql)/*safe-for-%s*/); blob_reset(&sql); #define MX_VAL 40 #define MX_NM 28 #define MX_LONGNM 60 while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q,0); int nName = db_column_bytes(&q,0); const char *zValue = db_column_text(&q,1); int szValue = db_column_bytes(&q,1); const char *zMTime = db_column_text(&q,2); for(i=j=0; j<MX_VAL && zValue[i]; i++){ unsigned char c = (unsigned char)zValue[i]; if( c>=' ' && c<='~' ){ zTrans[j++] = c; }else{ zTrans[j++] = '\\'; if( c=='\n' ){ zTrans[j++] = 'n'; }else if( c=='\r' ){ zTrans[j++] = 'r'; }else if( c=='\t' ){ zTrans[j++] = 't'; }else{ zTrans[j++] = '0' + ((c>>6)&7); zTrans[j++] = '0' + ((c>>3)&7); zTrans[j++] = '0' + (c&7); } } } zTrans[j] = 0; if( i<szValue ){ sqlite3_snprintf(sizeof(zTrans)-j, (char*)zTrans+j, "...+%d", szValue-i); j += (int)strlen((char*)zTrans+j); } if( showMtime ){ fossil_print("%s:%*s%s\n", zName, 58-nName, "", zMTime); }else if( nName<MX_NM-2 ){ fossil_print("%s:%*s%s\n", zName, MX_NM-1-nName, "", zTrans); }else if( nName<MX_LONGNM-2 && j<10 ){ fossil_print("%s:%*s%s\n", zName, MX_LONGNM-1-nName, "", zTrans); }else{ fossil_print("%s:\n%*s%s\n", zName, MX_NM, "", zTrans); } } db_finalize(&q); if( zPattern && doUnset ){ prompt_user("Delete all of the above? (y/N)? ", &ans); if( blob_str(&ans)[0]=='y' || blob_str(&ans)[0]=='Y' ){ db_multi_exec("DELETE FROM config WHERE name GLOB %Q", zPattern); } blob_reset(&ans); } } /* ** COMMAND: test-var-get ** ** Usage: %fossil test-var-get VAR ?FILE? ** ** Write the text of the VAR variable into FILE. If FILE is "-" ** or is omitted then output goes to standard output. VAR can be a ** GLOB pattern. ** ** If not in an open check-out, use the -R REPO option to specify a ** a repository. */ void test_var_get_cmd(void){ const char *zVar; const char *zFile; int n; Blob x; db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); verify_all_options(); if( g.argc<3 ){ usage("VAR ?FILE?"); } zVar = g.argv[2]; zFile = g.argc>=4 ? g.argv[3] : "-"; n = db_int(0, "SELECT count(*) FROM config WHERE name GLOB %Q", zVar); if( n==0 ){ fossil_fatal("no match for %Q", zVar); } if( n>1 ){ fossil_fatal("multiple matches: %s", db_text(0, "SELECT group_concat(quote(name),', ') FROM (" " SELECT name FROM config WHERE name GLOB %Q ORDER BY 1)", zVar)); } blob_init(&x,0,0); db_blob(&x, "SELECT value FROM config WHERE name GLOB %Q", zVar); blob_write_to_file(&x, zFile); } /* ** COMMAND: test-var-set ** ** Usage: %fossil test-var-set VAR ?VALUE? ?--file FILE? ** ** Store VALUE or the content of FILE (exactly one of which must be ** supplied) into variable VAR. Use a FILE of "-" to read from ** standard input. ** ** WARNING: changing the value of a variable can interfere with the ** operation of Fossil. Be sure you know what you are doing. ** ** Use "--blob FILE" instead of "--file FILE" to load a binary blob ** such as a GIF. */ void test_var_set_cmd(void){ const char *zVar; const char *zFile; const char *zBlob; Blob x; Stmt ins; zFile = find_option("file",0,1); zBlob = find_option("blob",0,1); db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); verify_all_options(); if( g.argc<3 || (zFile==0 && zBlob==0 && g.argc<4) ){ usage("VAR ?VALUE? ?--file FILE?"); } zVar = g.argv[2]; if( zFile ){ if( zBlob ) fossil_fatal("cannot do both --file or --blob"); blob_read_from_file(&x, zFile); }else if( zBlob ){ blob_read_from_file(&x, zBlob); }else{ blob_init(&x,g.argv[3],-1); } db_prepare(&ins, "REPLACE INTO config(name,value,mtime)" "VALUES(%Q,:val,now())", zVar); if( zBlob ){ db_bind_blob(&ins, ":val", &x); }else{ db_bind_text(&ins, ":val", blob_str(&x)); } db_step(&ins); db_finalize(&ins); blob_reset(&x); } |
Changes to src/content.c.
︙ | ︙ | |||
112 113 114 115 116 117 118 | bag_clear(&contentCache.available); bag_clear(&contentCache.inCache); contentCache.n = 0; contentCache.szTotal = 0; } /* | | | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | bag_clear(&contentCache.available); bag_clear(&contentCache.inCache); contentCache.n = 0; contentCache.szTotal = 0; } /* ** Return the srcid associated with rid. Or return 0 if rid is ** original content and not a delta. */ static int findSrcid(int rid){ static Stmt q; int srcid; db_static_prepare(&q, "SELECT srcid FROM delta WHERE rid=:rid"); db_bind_int(&q, ":rid", rid); |
︙ | ︙ | |||
152 153 154 155 156 157 158 | ** Check to see if content is available for artifact "rid". Return ** true if it is. Return false if rid is a phantom or depends on ** a phantom. */ int content_is_available(int rid){ int srcid; int depth = 0; /* Limit to recursion depth */ | | | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | ** Check to see if content is available for artifact "rid". Return ** true if it is. Return false if rid is a phantom or depends on ** a phantom. */ int content_is_available(int rid){ int srcid; int depth = 0; /* Limit to recursion depth */ while( depth++ < 10000000 ){ if( bag_find(&contentCache.missing, rid) ){ return 0; } if( bag_find(&contentCache.available, rid) ){ return 1; } if( content_size(rid, -1)<0 ){ |
︙ | ︙ | |||
386 387 388 389 390 391 392 | while( rid ){ int nChildUsed = 0; int i; /* Parse the object rid itself */ if( linkFlag ){ content_get(rid, &content); | | | | | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | while( rid ){ int nChildUsed = 0; int i; /* Parse the object rid itself */ if( linkFlag ){ content_get(rid, &content); manifest_crosslink(rid, &content, MC_NONE); assert( blob_is_reset(&content) ); } /* Parse all delta-manifests that depend on baseline-manifest rid */ db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); while( db_step(&q)==SQLITE_ROW ){ int child = db_column_int(&q, 0); if( nChildUsed>=nChildAlloc ){ nChildAlloc = nChildAlloc*2 + 10; aChild = fossil_realloc(aChild, nChildAlloc*sizeof(aChild)); } aChild[nChildUsed++] = child; } db_finalize(&q); for(i=0; i<nChildUsed; i++){ content_get(aChild[i], &content); manifest_crosslink(aChild[i], &content, MC_NONE); assert( blob_is_reset(&content) ); } if( nChildUsed ){ db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); } /* Recursively dephantomize all artifacts that are derived by ** delta from artifact rid and which have not already been ** cross-linked. */ nChildUsed = 0; db_prepare(&q, "SELECT rid FROM delta" " WHERE srcid=%d" " AND NOT EXISTS(SELECT 1 FROM mlink WHERE mid=delta.rid)", rid ); while( db_step(&q)==SQLITE_ROW ){ int child = db_column_int(&q, 0); |
︙ | ︙ | |||
453 454 455 456 457 458 459 | } /* ** Write content into the database. Return the record ID. If the ** content is already in the database, just return the record ID. ** ** If srcId is specified, then pBlob is delta content from | | | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | } /* ** Write content into the database. Return the record ID. If the ** content is already in the database, just return the record ID. ** ** If srcId is specified, then pBlob is delta content from ** the srcId record. srcId might be a phantom. ** ** pBlob is normally uncompressed text. But if nBlob>0 then the ** pBlob value has already been compressed and nBlob is its uncompressed ** size. If nBlob>0 then zUuid must be valid. ** ** zUuid is the UUID of the artifact, if it is specified. When srcId is ** specified then zUuid must always be specified. If srcId is zero, |
︙ | ︙ | |||
484 485 486 487 488 489 490 | int size; int rid; Stmt s1; Blob cmpr; Blob hash; int markAsUnclustered = 0; int isDephantomize = 0; | | | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | int size; int rid; Stmt s1; Blob cmpr; Blob hash; int markAsUnclustered = 0; int isDephantomize = 0; assert( g.repositoryOpen ); assert( pBlob!=0 ); assert( srcId==0 || zUuid!=0 ); if( zUuid==0 ){ assert( nBlob==0 ); sha1sum_blob(pBlob, &hash); }else{ |
︙ | ︙ | |||
556 557 558 559 560 561 562 | isDephantomize = 1; content_mark_available(rid); } }else{ /* We are creating a new entry */ db_prepare(&s1, "INSERT INTO blob(rcvid,size,uuid,content)" | | | > | | | < | | | 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 | isDephantomize = 1; content_mark_available(rid); } }else{ /* We are creating a new entry */ db_prepare(&s1, "INSERT INTO blob(rcvid,size,uuid,content)" "VALUES(%d,%d,'%q',:data)", g.rcvid, size, blob_str(&hash) ); db_bind_blob(&s1, ":data", &cmpr); db_exec(&s1); rid = db_last_insert_rowid(); if( !pBlob ){ db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid); } } if( g.markPrivate || isPrivate ){ db_multi_exec("INSERT INTO private VALUES(%d)", rid); markAsUnclustered = 0; } if( nBlob==0 ) blob_reset(&cmpr); /* If the srcId is specified, then the data we just added is ** really a delta. Record this fact in the delta table. */ if( srcId ){ db_multi_exec("REPLACE INTO delta(rid,srcid) VALUES(%d,%d)", rid, srcId); } if( !isDephantomize && bag_find(&contentCache.missing, rid) && (srcId==0 || content_is_available(srcId)) ){ content_mark_available(rid); } if( isDephantomize ){ after_dephantomize(rid, 0); } /* Add the element to the unclustered table if has never been ** previously seen. */ if( markAsUnclustered ){ db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d)", rid); } |
︙ | ︙ | |||
626 627 628 629 630 631 632 | /* ** Create a new phantom with the given UUID and return its artifact ID. */ int content_new(const char *zUuid, int isPrivate){ int rid; static Stmt s1, s2, s3; | | | 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 | /* ** Create a new phantom with the given UUID and return its artifact ID. */ int content_new(const char *zUuid, int isPrivate){ int rid; static Stmt s1, s2, s3; assert( g.repositoryOpen ); db_begin_transaction(); if( uuid_is_shunned(zUuid) ){ db_end_transaction(0); return 0; } db_static_prepare(&s1, |
︙ | ︙ | |||
725 726 727 728 729 730 731 | int rc; db_static_prepare(&s1, "SELECT 1 FROM private WHERE rid=:rid" ); db_bind_int(&s1, ":rid", rid); rc = db_step(&s1); db_reset(&s1); | | | | 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 | int rc; db_static_prepare(&s1, "SELECT 1 FROM private WHERE rid=:rid" ); db_bind_int(&s1, ":rid", rid); rc = db_step(&s1); db_reset(&s1); return rc==SQLITE_ROW; } /* ** Make sure an artifact is public. */ void content_make_public(int rid){ static Stmt s1; db_static_prepare(&s1, "DELETE FROM private WHERE rid=:rid" ); db_bind_int(&s1, ":rid", rid); |
︙ | ︙ | |||
756 757 758 759 760 761 762 | ** the source of the delta. It is OK to delta private->private and ** public->private and public->public. Just no private->public delta. ** ** If srcid is a delta that depends on rid, then srcid is ** converted to undeltaed text. ** ** If either rid or srcid contain less than 50 bytes, or if the | | | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 | ** the source of the delta. It is OK to delta private->private and ** public->private and public->public. Just no private->public delta. ** ** If srcid is a delta that depends on rid, then srcid is ** converted to undeltaed text. ** ** If either rid or srcid contain less than 50 bytes, or if the ** resulting delta does not achieve a compression of at least 25% ** the rid is left untouched. ** ** Return 1 if a delta is made and 0 if no delta occurs. */ int content_deltify(int rid, int srcid, int force){ int s; Blob data, src, delta; |
︙ | ︙ | |||
881 882 883 884 885 886 887 | fossil_print( "public artifact %S (%d) is a delta from private artifact %S (%d)\n", zId, rid, zSrc, srcid ); nErr++; } db_finalize(&q); | | | 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 | fossil_print( "public artifact %S (%d) is a delta from private artifact %S (%d)\n", zId, rid, zSrc, srcid ); nErr++; } db_finalize(&q); db_prepare(&q, "SELECT rid, uuid, size FROM blob ORDER BY rid"); total = db_int(0, "SELECT max(rid) FROM blob"); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char *zUuid = db_column_text(&q, 1); int size = db_column_int(&q, 2); n1++; |
︙ | ︙ | |||
943 944 945 946 947 948 949 | blob_reset(&cksum); n2++; } db_finalize(&q); fossil_print("%d non-phantom blobs (out of %d total) checked: %d errors\n", n2, n1, nErr); if( bParse ){ | | | | 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 | blob_reset(&cksum); n2++; } db_finalize(&q); fossil_print("%d non-phantom blobs (out of %d total) checked: %d errors\n", n2, n1, nErr); if( bParse ){ static const char *const azType[] = { 0, "manifest", "cluster", "control", "wiki", "ticket", "attachment", "event" }; int i; fossil_print("%d total control artifacts\n", nCA); for(i=1; i<count(azType); i++){ if( anCA[i] ) fossil_print(" %d %ss\n", anCA[i], azType[i]); } } } |
︙ | ︙ | |||
1017 1018 1019 1020 1021 1022 1023 | rc = 1; } db_reset(&q); if( rc ){ const char *zCFType = "control artifact"; char *zSrc; char *zDate; | | | 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 | rc = 1; } db_reset(&q); if( rc ){ const char *zCFType = "control artifact"; char *zSrc; char *zDate; const char *zErrType = "MISSING"; if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ if( flags & MISSING_SHUNNED ) return 0; zErrType = "SHUNNED"; } switch( p->type ){ case CFTYPE_MANIFEST: zCFType = "check-in"; break; case CFTYPE_CLUSTER: zCFType = "cluster"; break; |
︙ | ︙ | |||
1048 1049 1050 1051 1052 1053 1054 | fossil_print("%s: %s\n %s %s %S (%d) %s\n", zErrType, zUuid, zRole, zCFType, zSrc, p->rid, zDate); if( zDetail && zDetail[0] ){ fossil_print(" %s\n", zDetail); } fossil_free(zSrc); fossil_free(zDate); | | | 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 | fossil_print("%s: %s\n %s %s %S (%d) %s\n", zErrType, zUuid, zRole, zCFType, zSrc, p->rid, zDate); if( zDetail && zDetail[0] ){ fossil_print(" %s\n", zDetail); } fossil_free(zSrc); fossil_free(zDate); rc = 1; } return rc; } /* ** COMMAND: test-missing ** |
︙ | ︙ | |||
1095 1096 1097 1098 1099 1100 1101 | content_get(rid, &content); p = manifest_parse(&content, rid, 0); if( p ){ nArtifact++; nErr += check_exists(p->zBaseline, flags, p, "baseline of", 0); nErr += check_exists(p->zAttachSrc, flags, p, "file of", 0); for(i=0; i<p->nFile; i++){ | | | | 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 | content_get(rid, &content); p = manifest_parse(&content, rid, 0); if( p ){ nArtifact++; nErr += check_exists(p->zBaseline, flags, p, "baseline of", 0); nErr += check_exists(p->zAttachSrc, flags, p, "file of", 0); for(i=0; i<p->nFile; i++){ nErr += check_exists(p->aFile[i].zUuid, flags, p, "file of", p->aFile[i].zName); } for(i=0; i<p->nParent; i++){ nErr += check_exists(p->azParent[i], flags, p, "parent of", 0); } for(i=0; i<p->nCherrypick; i++){ nErr += check_exists(p->aCherrypick[i].zCPTarget+1, flags, p, "cherry-pick target of", 0); nErr += check_exists(p->aCherrypick[i].zCPBase, flags, p, "cherry-pick baseline of", 0); } for(i=0; i<p->nCChild; i++){ nErr += check_exists(p->azCChild[i], flags, p, "in", 0); } for(i=0; i<p->nTag; i++){ nErr += check_exists(p->aTag[i].zUuid, flags, p, "target of", 0); } manifest_destroy(p); } } db_finalize(&q); if( nErr>0 || quietFlag==0 ){ fossil_print("%d missing or shunned references in %d control artifacts\n", nErr, nArtifact); } } |
Changes to src/db.c.
︙ | ︙ | |||
42 43 44 45 46 47 48 | #if INTERFACE /* ** An single SQL statement is represented as an instance of the following ** structure. */ struct Stmt { Blob sql; /* The SQL for this statement */ | | < < < < | < | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | #if INTERFACE /* ** An single SQL statement is represented as an instance of the following ** structure. */ struct Stmt { Blob sql; /* The SQL for this statement */ sqlite3_stmt *pStmt; /* The results of sqlite3_prepare_v2() */ Stmt *pNext, *pPrev; /* List of all unfinalized statements */ int nStep; /* Number of sqlite3_step() calls */ }; /* ** Copy this to initialize a Stmt object to a clean/empty state. This ** is useful to help avoid assertions when performing cleanup in some ** error handling cases. */ #define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0} #endif /* INTERFACE */ const struct Stmt empty_Stmt = empty_Stmt_m; /* ** Call this routine when a database error occurs. */ static void db_err(const char *zFormat, ...){ va_list ap; char *z; int rc = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); #ifdef FOSSIL_ENABLE_JSON if( g.json.isJsonMode ){ json_err( 0, z, 1 ); if( g.isHTTP ){ rc = 0 /* avoid HTTP 500 */; } } else #endif /* FOSSIL_ENABLE_JSON */ if( g.xferPanic ){ cgi_reset_content(); @ error Database\serror:\s%F(z) cgi_reply(); } else if( g.cgiOutput ){ g.cgiOutput = 0; cgi_printf("<h1>Database Error</h1>\n<p>%h</p>\n", z); cgi_reply(); }else{ fprintf(stderr, "%s: %s\n", g.argv[0], z); } free(z); db_force_rollback(); fossil_exit(rc); } /* ** All static variable that a used by only this file are gathered into ** the following structure. */ static struct DbLocalData { int nBegin; /* Nesting depth of BEGIN */ int doRollback; /* True to force a rollback */ int nCommitHook; /* Number of commit hooks */ Stmt *pAllStmt; /* List of all unfinalized statements */ int nPrepare; /* Number of calls to sqlite3_prepare_v2() */ int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */ struct sCommitHook { int (*xHook)(void); /* Functions to call at db_end_transaction() */ int sequence; /* Call functions in sequence order */ } aHook[5]; char *azDeleteOnFail[3]; /* Files to delete on a failure */ char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */ |
︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 | if( g.db==0 ) return; if( db.nBegin<=0 ) return; if( rollbackFlag ) db.doRollback = 1; db.nBegin--; if( db.nBegin==0 ){ int i; if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){ while( db.nBeforeCommit ){ db.nBeforeCommit--; | > | | > | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | if( g.db==0 ) return; if( db.nBegin<=0 ) return; if( rollbackFlag ) db.doRollback = 1; db.nBegin--; if( db.nBegin==0 ){ int i; if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){ i = 0; while( db.nBeforeCommit ){ db.nBeforeCommit--; sqlite3_exec(g.db, db.azBeforeCommit[i], 0, 0, 0); sqlite3_free(db.azBeforeCommit[i]); i++; } leaf_do_pending_checks(); } for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){ db.doRollback |= db.aHook[i].xHook(); } while( db.pAllStmt ){ db_finalize(db.pAllStmt); } db_multi_exec("%s", db.doRollback ? "ROLLBACK" : "COMMIT"); db.doRollback = 0; } } /* ** Force a rollback and shutdown the database */ |
︙ | ︙ | |||
313 314 315 316 317 318 319 320 321 322 323 324 325 326 | } int db_bind_double(Stmt *pStmt, const char *zParamName, double rValue){ return sqlite3_bind_double(pStmt->pStmt, paramIdx(pStmt, zParamName), rValue); } int db_bind_text(Stmt *pStmt, const char *zParamName, const char *zValue){ return sqlite3_bind_text(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue, -1, SQLITE_STATIC); } int db_bind_null(Stmt *pStmt, const char *zParamName){ return sqlite3_bind_null(pStmt->pStmt, paramIdx(pStmt, zParamName)); } int db_bind_blob(Stmt *pStmt, const char *zParamName, Blob *pBlob){ return sqlite3_bind_blob(pStmt->pStmt, paramIdx(pStmt, zParamName), blob_buffer(pBlob), blob_size(pBlob), SQLITE_STATIC); | > > > > | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | } int db_bind_double(Stmt *pStmt, const char *zParamName, double rValue){ return sqlite3_bind_double(pStmt->pStmt, paramIdx(pStmt, zParamName), rValue); } int db_bind_text(Stmt *pStmt, const char *zParamName, const char *zValue){ return sqlite3_bind_text(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue, -1, SQLITE_STATIC); } int db_bind_text16(Stmt *pStmt, const char *zParamName, const char *zValue){ return sqlite3_bind_text16(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue, -1, SQLITE_STATIC); } int db_bind_null(Stmt *pStmt, const char *zParamName){ return sqlite3_bind_null(pStmt->pStmt, paramIdx(pStmt, zParamName)); } int db_bind_blob(Stmt *pStmt, const char *zParamName, Blob *pBlob){ return sqlite3_bind_blob(pStmt->pStmt, paramIdx(pStmt, zParamName), blob_buffer(pBlob), blob_size(pBlob), SQLITE_STATIC); |
︙ | ︙ | |||
341 342 343 344 345 346 347 348 349 350 351 352 353 354 | */ int db_step(Stmt *pStmt){ int rc; rc = sqlite3_step(pStmt->pStmt); pStmt->nStep++; return rc; } /* ** Print warnings if a query is inefficient. */ static void db_stats(Stmt *pStmt){ #ifdef FOSSIL_DEBUG int c1, c2, c3; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | */ int db_step(Stmt *pStmt){ int rc; rc = sqlite3_step(pStmt->pStmt); pStmt->nStep++; return rc; } /* ** Steps the SQL statement until there are no more rows. Returns the ** total number of rows processed by this function. If the pazValue1 ** parameter is non-zero, captures the iCol1'th column value from each ** row (as text) and stores the resulting final array pointer into it. ** If the pazValue2 parameter is non-zero, captures the iCol2'th column ** value from each row (as text) and stores the resulting final array ** pointer into it. The caller of this function is responsible for ** calling the db_all_column_free() function later, passing it the ** result of this function along with the values for both the pazValue1 ** and pazValue2 paramters. */ int db_all_column_text( Stmt *pStmt, /* The statement handle. */ int iCol1, /* The first column number to fetch from the results. */ char ***pazValue1, /* Array of iCol1'th column values from query. */ int iCol2, /* The second column number to fetch from the results. */ char ***pazValue2 /* Array of iCol2'th column values from query. */ ){ int count = 0; char **azValue1 = 0; char **azValue2 = 0; while( db_step(pStmt)==SQLITE_ROW ){ count++; if( pazValue1 ){ azValue1 = fossil_realloc(azValue1, count * sizeof(char*)); azValue1[count - 1] = fossil_strdup(db_column_text(pStmt, iCol1)); } if( pazValue2 ){ azValue2 = fossil_realloc(azValue2, count * sizeof(char*)); azValue2[count - 1] = fossil_strdup(db_column_text(pStmt, iCol2)); } } if( pazValue1 ){ *pazValue1 = azValue1; } if( pazValue2 ){ *pazValue2 = azValue2; } return count; } /* ** This function frees all the storage that was allocated by the ** db_all_column_text() function for a particular column. */ void db_all_column_free( int count, /* Number of string elements in the arrays. */ char ***pazValue /* Array of column values from query. */ ){ if( pazValue ){ char **azValue = *pazValue; int i; for(i=0; i<count; i++){ fossil_free(azValue[i]); azValue[i] = 0; } fossil_free(azValue); *pazValue = 0; } } /* ** Print warnings if a query is inefficient. */ static void db_stats(Stmt *pStmt){ #ifdef FOSSIL_DEBUG int c1, c2, c3; |
︙ | ︙ | |||
418 419 420 421 422 423 424 425 426 427 428 429 430 431 | return sqlite3_changes(g.db); } /* ** Extract text, integer, or blob values from the N-th column of the ** current row. */ int db_column_bytes(Stmt *pStmt, int N){ return sqlite3_column_bytes(pStmt->pStmt, N); } int db_column_int(Stmt *pStmt, int N){ return sqlite3_column_int(pStmt->pStmt, N); } i64 db_column_int64(Stmt *pStmt, int N){ | > > > | 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 | return sqlite3_changes(g.db); } /* ** Extract text, integer, or blob values from the N-th column of the ** current row. */ int db_column_type(Stmt *pStmt, int N){ return sqlite3_column_type(pStmt->pStmt, N); } int db_column_bytes(Stmt *pStmt, int N){ return sqlite3_column_bytes(pStmt->pStmt, N); } int db_column_int(Stmt *pStmt, int N){ return sqlite3_column_int(pStmt->pStmt, N); } i64 db_column_int64(Stmt *pStmt, int N){ |
︙ | ︙ | |||
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | int db_exec(Stmt *pStmt){ int rc; while( (rc = db_step(pStmt))==SQLITE_ROW ){} rc = db_reset(pStmt); db_check_result(rc); return rc; } /* ** Execute multiple SQL statements. */ int db_multi_exec(const char *zSql, ...){ Blob sql; int rc = SQLITE_OK; va_list ap; const char *z, *zEnd; sqlite3_stmt *pStmt; blob_init(&sql, 0, 0); va_start(ap, zSql); blob_vappendf(&sql, zSql, ap); va_end(ap); z = blob_str(&sql); while( rc==SQLITE_OK && z[0] ){ pStmt = 0; rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 | int db_exec(Stmt *pStmt){ int rc; while( (rc = db_step(pStmt))==SQLITE_ROW ){} rc = db_reset(pStmt); db_check_result(rc); return rc; } /* ** Print the output of one or more SQL queries on standard output. ** This routine is used for debugging purposes only. */ int db_debug(const char *zSql, ...){ Blob sql; int rc = SQLITE_OK; va_list ap; const char *z, *zEnd; sqlite3_stmt *pStmt; blob_init(&sql, 0, 0); va_start(ap, zSql); blob_vappendf(&sql, zSql, ap); va_end(ap); z = blob_str(&sql); while( rc==SQLITE_OK && z[0] ){ pStmt = 0; rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); if( rc!=SQLITE_OK ) break; if( pStmt ){ int nRow = 0; db.nPrepare++; while( sqlite3_step(pStmt)==SQLITE_ROW ){ int i, n; if( nRow++ > 0 ) fossil_print("\n"); n = sqlite3_column_count(pStmt); for(i=0; i<n; i++){ fossil_print("%s = %s\n", sqlite3_column_name(pStmt, i), sqlite3_column_text(pStmt,i)); } } rc = sqlite3_finalize(pStmt); if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); } z = zEnd; } blob_reset(&sql); return rc; } /* ** Execute multiple SQL statements. */ int db_multi_exec(const char *zSql, ...){ Blob sql; int rc = SQLITE_OK; va_list ap; const char *z, *zEnd; sqlite3_stmt *pStmt; blob_init(&sql, 0, 0); va_start(ap, zSql); blob_vappendf(&sql, zSql, ap); va_end(ap); z = blob_str(&sql); while( rc==SQLITE_OK && z[0] ){ pStmt = 0; rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); if( rc ){ db_err("%s: {%s}", sqlite3_errmsg(g.db), z); }else if( pStmt ){ db.nPrepare++; while( sqlite3_step(pStmt)==SQLITE_ROW ){} rc = sqlite3_finalize(pStmt); if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); } z = zEnd; } |
︙ | ︙ | |||
622 623 624 625 626 627 628 | /* ** Execute a query. Return the first column of the first row ** of the result set as a string. Space to hold the string is ** obtained from malloc(). If the result set is empty, return ** zDefault instead. */ | | | 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 | /* ** Execute a query. Return the first column of the first row ** of the result set as a string. Space to hold the string is ** obtained from malloc(). If the result set is empty, return ** zDefault instead. */ char *db_text(const char *zDefault, const char *zSql, ...){ va_list ap; Stmt s; char *z; va_start(ap, zSql); db_vprepare(&s, 0, zSql, ap); va_end(ap); if( db_step(&s)==SQLITE_ROW ){ |
︙ | ︙ | |||
658 659 660 661 662 663 664 | const char *zSql; va_list ap; db = db_open(zFileName); sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); rc = sqlite3_exec(db, zSchema, 0, 0, 0); if( rc!=SQLITE_OK ){ | | | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | const char *zSql; va_list ap; db = db_open(zFileName); sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); rc = sqlite3_exec(db, zSchema, 0, 0, 0); if( rc!=SQLITE_OK ){ db_err("%s", sqlite3_errmsg(db)); } va_start(ap, zSchema); while( (zSql = va_arg(ap, const char*))!=0 ){ rc = sqlite3_exec(db, zSql, 0, 0, 0); if( rc!=SQLITE_OK ){ db_err("%s", sqlite3_errmsg(db)); } } va_end(ap); sqlite3_exec(db, "COMMIT", 0, 0, 0); sqlite3_close(db); } |
︙ | ︙ | |||
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 | sqlite3_value **argv ){ sqlite3_result_int64(context, time(0)); } /* ** Function to return the check-in time for a file. */ void db_checkin_mtime_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ i64 mtime; int rc = mtime_of_manifest_file(sqlite3_value_int(argv[0]), sqlite3_value_int(argv[1]), &mtime); if( rc==0 ){ sqlite3_result_int64(context, mtime); } } /* ** Open a database file. Return a pointer to the new database ** connection. An error results in process abort. */ LOCAL sqlite3 *db_open(const char *zDbName){ int rc; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < | < < < | | | > | > | | | 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 | sqlite3_value **argv ){ sqlite3_result_int64(context, time(0)); } /* ** Function to return the check-in time for a file. ** ** checkin_mtime(CKINID,RID) ** ** CKINID: The RID for the manifest for a check-in. ** RID: The RID of a file in CKINID for which the check-in time ** is desired. ** ** Returns: The check-in time in seconds since 1970. */ void db_checkin_mtime_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ i64 mtime; int rc = mtime_of_manifest_file(sqlite3_value_int(argv[0]), sqlite3_value_int(argv[1]), &mtime); if( rc==0 ){ sqlite3_result_int64(context, mtime); } } /* ** SQL wrapper around the symbolic_name_to_rid() C-language API. ** Examples: ** ** symbolic_name_to_rid('trunk'); ** symbolic_name_to_rid('trunk','w'); ** */ void db_sym2rid_function( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *arg; const char *type; if(1 != argc && 2 != argc){ sqlite3_result_error(context, "Expecting one or two arguments", -1); return; } arg = (const char*)sqlite3_value_text(argv[0]); if(!arg){ sqlite3_result_error(context, "Expecting a STRING argument", -1); }else{ int rid; type = (2==argc) ? (const char*)sqlite3_value_text(argv[1]) : 0; if(!type) type = "ci"; rid = symbolic_name_to_rid( arg, type ); if(rid<0){ sqlite3_result_error(context, "Symbolic name is ambiguous.", -1); }else if(0==rid){ sqlite3_result_null(context); }else{ sqlite3_result_int64(context, rid); } } } /* ** Register the SQL functions that are useful both to the internal ** representation and to the "fossil sql" command. */ void db_add_aux_functions(sqlite3 *db){ sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0, db_checkin_mtime_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, db_sym2rid_function, 0, 0); sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, db_now_function, 0, 0); } /* ** Open a database file. Return a pointer to the new database ** connection. An error results in process abort. */ LOCAL sqlite3 *db_open(const char *zDbName){ int rc; sqlite3 *db; if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName); rc = sqlite3_open_v2( zDbName, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, g.zVfsName ); if( rc!=SQLITE_OK ){ db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); } sqlite3_busy_timeout(db, 5000); sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); sqlite3_create_function( db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 ); sqlite3_create_function( db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 ); if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); db_add_aux_functions(db); re_add_sql_func(db); /* The REGEXP operator */ foci_register(db); /* The "files_of_checkin" virtual table */ sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); return db; } /* ** Detaches the zLabel database. */ void db_detach(const char *zLabel){ db_multi_exec("DETACH DATABASE %Q", zLabel); } /* ** zDbName is the name of a database file. Attach zDbName using ** the name zLabel. */ void db_attach(const char *zDbName, const char *zLabel){ db_multi_exec("ATTACH DATABASE %Q AS %Q", zDbName, zLabel); } /* ** zDbName is the name of a database file. If no other database ** file is open, then open this one. If another database file is ** already open, then attach zDbName using the name zLabel. */ |
︙ | ︙ | |||
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 | if( pWasAttached ) *pWasAttached = 0; }else{ assert( g.zMainDbType!=0 ); db_attach(zDbName, zLabel); if( pWasAttached ) *pWasAttached = 1; } } /* ** Open the user database in "~/.fossil". Create the database anew if ** it does not already exist. ** ** If the useAttach flag is 0 (the usual case) then the user database is ** opened on a separate database connection g.dbConfig. This prevents ** the ~/.fossil database from becoming locked on long check-in or sync ** operations which hold an exclusive transaction. In a few cases, though, ** it is convenient for the ~/.fossil to be attached to the main database ** connection so that we can join between the various databases. In that ** case, invoke this routine with useAttach as 1. */ void db_open_config(int useAttach){ char *zDbName; char *zHome; | > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | | | | | | | | | > | | | > | > | | < < < > > > < < > > > > > > > > > > > > > > > > > > > > > > > > > > < < | < < | < < < < < | 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 | if( pWasAttached ) *pWasAttached = 0; }else{ assert( g.zMainDbType!=0 ); db_attach(zDbName, zLabel); if( pWasAttached ) *pWasAttached = 1; } } /* ** Close the user database. */ void db_close_config(){ if( g.useAttach ){ db_detach("configdb"); g.useAttach = 0; g.zConfigDbName = 0; }else if( g.dbConfig ){ sqlite3_wal_checkpoint(g.dbConfig, 0); sqlite3_close(g.dbConfig); g.dbConfig = 0; g.zConfigDbType = 0; g.zConfigDbName = 0; }else if( g.db && fossil_strcmp(g.zMainDbType, "configdb")==0 ){ sqlite3_wal_checkpoint(g.db, 0); sqlite3_close(g.db); g.db = 0; g.zMainDbType = 0; g.zConfigDbName = 0; } } /* ** Open the user database in "~/.fossil". Create the database anew if ** it does not already exist. ** ** If the useAttach flag is 0 (the usual case) then the user database is ** opened on a separate database connection g.dbConfig. This prevents ** the ~/.fossil database from becoming locked on long check-in or sync ** operations which hold an exclusive transaction. In a few cases, though, ** it is convenient for the ~/.fossil to be attached to the main database ** connection so that we can join between the various databases. In that ** case, invoke this routine with useAttach as 1. */ void db_open_config(int useAttach){ char *zDbName; char *zHome; if( g.zConfigDbName ){ if( useAttach==g.useAttach ) return; db_close_config(); } zHome = fossil_getenv("FOSSIL_HOME"); #if defined(_WIN32) || defined(__CYGWIN__) if( zHome==0 ){ zHome = fossil_getenv("LOCALAPPDATA"); if( zHome==0 ){ zHome = fossil_getenv("APPDATA"); if( zHome==0 ){ char *zDrive = fossil_getenv("HOMEDRIVE"); char *zPath = fossil_getenv("HOMEPATH"); if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); } } } if( zHome==0 ){ fossil_fatal("cannot locate home directory - please set the " "FOSSIL_HOME, LOCALAPPDATA, APPDATA, or HOMEPATH " "environment variables"); } #else if( zHome==0 ){ zHome = fossil_getenv("HOME"); } if( zHome==0 ){ fossil_fatal("cannot locate home directory - please set the " "FOSSIL_HOME or HOME environment variables"); } #endif if( file_isdir(zHome)!=1 ){ fossil_fatal("invalid home directory: %s", zHome); } #if defined(_WIN32) || defined(__CYGWIN__) /* . filenames give some window systems problems and many apps problems */ zDbName = mprintf("%//_fossil", zHome); #else zDbName = mprintf("%s/.fossil", zHome); #endif if( file_size(zDbName)<1024*3 ){ if( file_access(zHome, W_OK) ){ fossil_fatal("home directory %s must be writeable", zHome); } db_init_database(zDbName, zConfigSchema, (char*)0); } if( file_access(zDbName, W_OK) ){ fossil_fatal("configuration file %s must be writeable", zDbName); } if( useAttach ){ db_open_or_attach(zDbName, "configdb", &g.useAttach); g.dbConfig = 0; g.zConfigDbType = 0; }else{ g.useAttach = 0; g.dbConfig = db_open(zDbName); g.zConfigDbType = "configdb"; } g.zConfigDbName = zDbName; } /* ** Return TRUE if zTable exists. */ int db_table_exists( const char *zDb, /* One of: NULL, "configdb", "localdb", "repository" */ const char *zTable /* Name of table */ ){ return sqlite3_table_column_metadata(g.db, zDb ? db_name(zDb) : 0, zTable, 0, 0, 0, 0, 0, 0)==SQLITE_OK; } /* ** Return TRUE if zTable exists and contains column zColumn. ** Return FALSE if zTable does not exist or if zTable exists ** but lacks zColumn. */ int db_table_has_column( const char *zDb, /* One of: NULL, "config", "localdb", "repository" */ const char *zTable, /* Name of table */ const char *zColumn /* Name of column in table */ ){ return sqlite3_table_column_metadata(g.db, zDb ? db_name(zDb) : 0, zTable, zColumn, 0, 0, 0, 0, 0)==SQLITE_OK; } /* ** Returns TRUE if zTable exists in the local database but lacks column ** zColumn */ static int db_local_table_exists_but_lacks_column( const char *zTable, const char *zColumn ){ return db_table_exists(db_name("localdb"), zTable) && !db_table_has_column(db_name("localdb"), zTable, zColumn); } /* ** If zDbName is a valid local database file, open it and return ** true. If it is not a valid local database file, return 0. */ static int isValidLocalDb(const char *zDbName){ |
︙ | ︙ | |||
894 895 896 897 898 899 900 | " WHERE name=='vfile'", db_name("localdb")); if( zVFileDef==0 ) return 0; /* If the "isexe" column is missing from the vfile table, then ** add it now. This code added on 2010-03-06. After all users have ** upgraded, this code can be safely deleted. */ | | | | 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 | " WHERE name=='vfile'", db_name("localdb")); if( zVFileDef==0 ) return 0; /* If the "isexe" column is missing from the vfile table, then ** add it now. This code added on 2010-03-06. After all users have ** upgraded, this code can be safely deleted. */ if( sqlite3_strglob("* isexe *", zVFileDef)!=0 ){ db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0"); } /* If "islink"/"isLink" columns are missing from tables, then ** add them now. This code added on 2011-01-17 and 2011-08-27. ** After all users have upgraded, this code can be safely deleted. */ if( sqlite3_strglob("* islink *", zVFileDef)!=0 ){ db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0"); if( db_local_table_exists_but_lacks_column("stashfile", "isLink") ){ db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOL DEFAULT 0"); } if( db_local_table_exists_but_lacks_column("undo", "isLink") ){ db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0"); } |
︙ | ︙ | |||
937 938 939 940 941 942 943 | ** This routine always opens the user database regardless of whether or ** not the repository database is found. If the _FOSSIL_ or .fslckout file ** is found, it is attached to the open database connection too. */ int db_open_local(const char *zDbName){ int i, n; char zPwd[2000]; | | | < > | | | | 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 | ** This routine always opens the user database regardless of whether or ** not the repository database is found. If the _FOSSIL_ or .fslckout file ** is found, it is attached to the open database connection too. */ int db_open_local(const char *zDbName){ int i, n; char zPwd[2000]; static const char *(aDbName[]) = { "_FOSSIL_", ".fslckout", ".fos" }; if( g.localOpen ) return 1; file_getcwd(zPwd, sizeof(zPwd)-20); n = strlen(zPwd); while( n>0 ){ for(i=0; i<count(aDbName); i++){ sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "/%s", aDbName[i]); if( isValidLocalDb(zPwd) ){ /* Found a valid checkout database file */ g.zLocalDbName = mprintf("%s", zPwd); zPwd[n] = 0; while( n>0 && zPwd[n-1]=='/' ){ n--; zPwd[n] = 0; } g.zLocalRoot = mprintf("%s/", zPwd); g.localOpen = 1; db_open_config(0); db_open_repository(zDbName); return 1; } } n--; while( n>1 && zPwd[n]!='/' ){ n--; } while( n>1 && zPwd[n-1]=='/' ){ n--; } zPwd[n] = 0; } /* A checkout database file could not be found */ return 0; } |
︙ | ︙ | |||
1003 1004 1005 1006 1007 1008 1009 | zDbName = db_repository_filename(); } if( zDbName==0 ){ db_err("unable to find the name of a repository database"); } } if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){ | | < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 | zDbName = db_repository_filename(); } if( zDbName==0 ){ db_err("unable to find the name of a repository database"); } } if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){ if( file_access(zDbName, F_OK) ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND; #endif fossil_panic("repository does not exist or" " is in an unreadable directory: %s", zDbName); }else if( file_access(zDbName, R_OK) ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DENIED; #endif fossil_panic("read permission denied for repository %s", zDbName); }else{ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_VALID; #endif fossil_panic("not a valid repository: %s", zDbName); } } g.zRepositoryName = mprintf("%s", zDbName); db_open_or_attach(g.zRepositoryName, "repository", 0); g.repositoryOpen = 1; /* Cache "allow-symlinks" option, because we'll need it on every stat call */ g.allowSymlinks = db_get_boolean("allow-symlinks", 0); g.zAuxSchema = db_get("aux-schema",""); /* Verify that the PLINK table has a new column added by the ** 2014-11-28 schema change. Create it if necessary. This code ** can be removed in the future, once all users have upgraded to the ** 2014-11-28 or later schema. */ if( !db_table_has_column("repository","plink","baseid") ){ db_multi_exec( "ALTER TABLE %s.plink ADD COLUMN baseid;", db_name("repository") ); } /* Verify that the MLINK table has the newer columns added by the ** 2015-01-24 schema change. Create them if necessary. This code ** can be removed in the future, once all users have upgraded to the ** 2015-01-24 or later schema. */ if( !db_table_has_column("repository","mlink","isaux") ){ db_begin_transaction(); db_multi_exec( "ALTER TABLE %s.mlink ADD COLUMN pmid INTEGER DEFAULT 0;" "ALTER TABLE %s.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;", db_name("repository"), db_name("repository") ); db_end_transaction(0); } } /* ** Flags for the db_find_and_open_repository() function. */ #if INTERFACE #define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */ #define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */ #endif /* ** Try to find the repository and open it. Use the -R or --repository ** option to locate the repository. If no such option is available, then ** use the repository of the open checkout if there is one. ** ** Error out if the repository cannot be opened. */ void db_find_and_open_repository(int bFlags, int nArgUsed){ const char *zRep = find_repository_option(); if( zRep && file_isdir(zRep)==1 ){ goto rep_not_found; } if( zRep==0 && nArgUsed && g.argc==nArgUsed+1 ){ zRep = g.argv[nArgUsed]; } if( zRep==0 ){ if( db_open_local(0)==0 ){ goto rep_not_found; } |
︙ | ︙ | |||
1094 1095 1096 1097 1098 1099 1100 | return zDb; } /* ** Return TRUE if the schema is out-of-date */ int db_schema_is_outofdate(void){ | | < | | | | | | 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 | return zDb; } /* ** Return TRUE if the schema is out-of-date */ int db_schema_is_outofdate(void){ return strcmp(g.zAuxSchema,AUX_SCHEMA_MIN)<0 || strcmp(g.zAuxSchema,AUX_SCHEMA_MAX)>0; } /* ** Return true if the database is writeable */ int db_is_writeable(const char *zName){ return g.db!=0 && !sqlite3_db_readonly(g.db, db_name(zName)); } /* ** Verify that the repository schema is correct. If it is not correct, ** issue a fatal error and die. */ void db_verify_schema(void){ if( db_schema_is_outofdate() ){ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NEEDS_REBUILD; #endif fossil_warning("incorrect repository schema version: " "current repository schema version is \"%s\" " "but need versions between \"%s\" and \"%s\".", g.zAuxSchema, AUX_SCHEMA_MIN, AUX_SCHEMA_MAX); fossil_fatal("run \"fossil rebuild\" to fix this problem"); } } /* ** COMMAND: test-move-repository |
︙ | ︙ | |||
1141 1142 1143 1144 1145 1146 1147 | Blob repo; char *zRepo; if( g.argc!=3 ){ usage("PATHNAME"); } file_canonical_name(g.argv[2], &repo, 0); zRepo = blob_str(&repo); | | > | 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 | Blob repo; char *zRepo; if( g.argc!=3 ){ usage("PATHNAME"); } file_canonical_name(g.argv[2], &repo, 0); zRepo = blob_str(&repo); if( file_access(zRepo, F_OK) ){ fossil_fatal("no such file: %s", zRepo); } if( db_open_local(zRepo)==0 ){ fossil_fatal("not in a local checkout"); return; } db_open_or_attach(zRepo, "test_repo", 0); db_lset("repository", blob_str(&repo)); db_record_repository_filename(blob_str(&repo)); db_close(1); } /* ** Open the local database. If unable, exit with an error. */ |
︙ | ︙ | |||
1205 1206 1207 1208 1209 1210 1211 | fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare); } while( db.pAllStmt ){ db_finalize(db.pAllStmt); } db_end_transaction(1); pStmt = 0; | > > > > > > > > > > > > > > > > > | | | | > > > < < < < < | < > | | < | 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 | fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare); } while( db.pAllStmt ){ db_finalize(db.pAllStmt); } db_end_transaction(1); pStmt = 0; db_close_config(); /* If the localdb (the check-out database) is open and if it has ** a lot of unused free space, then VACUUM it as we shut down. */ if( g.localOpen && strcmp(db_name("localdb"),"main")==0 ){ int nFree = db_int(0, "PRAGMA main.freelist_count"); int nTotal = db_int(0, "PRAGMA main.page_count"); if( nFree>nTotal/4 ){ db_multi_exec("VACUUM;"); } } if( g.db ){ int rc; sqlite3_wal_checkpoint(g.db, 0); rc = sqlite3_close(g.db); if( rc==SQLITE_BUSY && reportErrors ){ while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){ fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt)); } } g.db = 0; g.zMainDbType = 0; } g.repositoryOpen = 0; g.localOpen = 0; assert( g.dbConfig==0 ); assert( g.useAttach==0 ); assert( g.zConfigDbName==0 ); assert( g.zConfigDbType==0 ); } /* ** Create a new empty repository database with the given name. ** ** Only the schema is initialized. The required VAR tables entries |
︙ | ︙ | |||
1277 1278 1279 1280 1281 1282 1283 | db_multi_exec( "UPDATE user SET cap='s', pw=lower(hex(randomblob(3)))" " WHERE login=%Q", zUser ); if( !setupUserOnly ){ db_multi_exec( "INSERT OR IGNORE INTO user(login,pw,cap,info)" | | | | | | | | | 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 | db_multi_exec( "UPDATE user SET cap='s', pw=lower(hex(randomblob(3)))" " WHERE login=%Q", zUser ); if( !setupUserOnly ){ db_multi_exec( "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('nobody','','gjorz','Nobody');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('developer','','dei','Dev');" "INSERT OR IGNORE INTO user(login,pw,cap,info)" " VALUES('reader','','kptw','Reader');" ); } } /* ** Return a pointer to a string that contains the RHS of an IN operator ** that will select CONFIG table names that are in the list of control ** settings. */ const char *db_setting_inop_rhs(){ Blob x; int i; const char *zSep = ""; blob_zero(&x); blob_append_sql(&x, "("); for(i=0; aSetting[i].name; i++){ blob_append_sql(&x, "%s%Q", zSep/*safe-for-%s*/, aSetting[i].name); zSep = ","; } blob_append_sql(&x, ")"); return blob_sql_text(&x); } /* ** Fill an empty repository database with the basic information for a ** repository. This function is shared between 'create_repository_cmd' ** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create ** new repositories. |
︙ | ︙ | |||
1329 1330 1331 1332 1333 1334 1335 | ** that is automatically created. If zInitialDate is 0 then no initial ** check-in is created. The makeServerCodes flag determines whether or ** not server and project codes are invented for this repository. */ void db_initial_setup( const char *zTemplate, /* Repository from which to copy settings. */ const char *zInitialDate, /* Initial date of repository. (ex: "now") */ | | < | | | | < | > | 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 | ** that is automatically created. If zInitialDate is 0 then no initial ** check-in is created. The makeServerCodes flag determines whether or ** not server and project codes are invented for this repository. */ void db_initial_setup( const char *zTemplate, /* Repository from which to copy settings. */ const char *zInitialDate, /* Initial date of repository. (ex: "now") */ const char *zDefaultUser /* Default user for the repository */ ){ char *zDate; Blob hash; Blob manifest; db_set("content-schema", CONTENT_SCHEMA, 0); db_set("aux-schema", AUX_SCHEMA_MAX, 0); db_set("rebuilt", get_version(), 0); db_multi_exec( "INSERT INTO config(name,value,mtime)" " VALUES('server-code', lower(hex(randomblob(20))),now());" "INSERT INTO config(name,value,mtime)" " VALUES('project-code', lower(hex(randomblob(20))),now());" ); if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); if( !db_is_global("timeline-plaintext") ){ db_set_int("timeline-plaintext", 1, 0); } db_create_default_users(0, zDefaultUser); if( zDefaultUser ) g.zLogin = zDefaultUser; user_select(); if( zTemplate ){ /* ** Copy all settings from the supplied template repository. */ db_multi_exec( "INSERT OR REPLACE INTO config" " SELECT name,value,mtime FROM settingSrc.config" " WHERE (name IN %s OR name IN %s)" " AND name NOT GLOB 'project-*'" " AND name NOT GLOB 'short-project-*';", configure_inop_rhs(CONFIGSET_ALL), db_setting_inop_rhs() ); db_multi_exec( "REPLACE INTO reportfmt SELECT * FROM settingSrc.reportfmt;" ); |
︙ | ︙ | |||
1410 1411 1412 1413 1414 1415 1416 | blob_appendf(&manifest, "T *branch * trunk\n"); blob_appendf(&manifest, "T *sym-trunk *\n"); blob_appendf(&manifest, "U %F\n", g.zLogin); md5sum_blob(&manifest, &hash); blob_appendf(&manifest, "Z %b\n", &hash); blob_reset(&hash); rid = content_put(&manifest); | | | 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 | blob_appendf(&manifest, "T *branch * trunk\n"); blob_appendf(&manifest, "T *sym-trunk *\n"); blob_appendf(&manifest, "U %F\n", g.zLogin); md5sum_blob(&manifest, &hash); blob_appendf(&manifest, "Z %b\n", &hash); blob_reset(&hash); rid = content_put(&manifest); manifest_crosslink(rid, &manifest, MC_NONE); } } /* ** COMMAND: new* ** COMMAND: init ** |
︙ | ︙ | |||
1441 1442 1443 1444 1445 1446 1447 | ** their associated permissions will not be copied; however, the system ** default users "anonymous", "nobody", "reader", "developer", and their ** associated permissions will be copied. ** ** Options: ** --template FILE copy settings from repository file ** --admin-user|-A USERNAME select given USERNAME as admin user | | > > | > > > > > > | | 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 | ** their associated permissions will not be copied; however, the system ** default users "anonymous", "nobody", "reader", "developer", and their ** associated permissions will be copied. ** ** Options: ** --template FILE copy settings from repository file ** --admin-user|-A USERNAME select given USERNAME as admin user ** --date-override DATETIME use DATETIME as time of the initial check-in ** ** See also: clone */ void create_repository_cmd(void){ char *zPassword; const char *zTemplate; /* Repository from which to copy settings */ const char *zDate; /* Date of the initial check-in */ const char *zDefaultUser; /* Optional name of the default user */ zTemplate = find_option("template",0,1); zDate = find_option("date-override",0,1); zDefaultUser = find_option("admin-user","A",1); /* We should be done with options.. */ verify_all_options(); if( g.argc!=3 ){ usage("REPOSITORY-NAME"); } if( -1 != file_size(g.argv[2]) ){ fossil_fatal("file already exists: %s", g.argv[2]); } db_create_repository(g.argv[2]); db_open_repository(g.argv[2]); db_open_config(0); if( zTemplate ) db_attach(zTemplate, "settingSrc"); db_begin_transaction(); if( zDate==0 ) zDate = "now"; db_initial_setup(zTemplate, zDate, zDefaultUser); db_end_transaction(0); if( zTemplate ) db_detach("settingSrc"); fossil_print("project-id: %s\n", db_get("project-code", 0)); fossil_print("server-id: %s\n", db_get("server-code", 0)); zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
︙ | ︙ | |||
1687 1688 1689 1690 1691 1692 1693 | g.zMainDbType = g.zConfigDbType; g.dbConfig = dbTemp; g.zConfigDbType = zTempDbType; } } /* | < | > > > > > | | > | | 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 | g.zMainDbType = g.zConfigDbType; g.dbConfig = dbTemp; g.zConfigDbType = zTempDbType; } } /* ** Try to read a versioned setting string from .fossil-settings/<name>. ** ** Return the text of the string if it is found. Return NULL if not ** found. ** ** If the zNonVersionedSetting parameter is not NULL then it holds the ** non-versioned value for this setting. If both a versioned and ad ** non-versioned value exist and are not equal, then a warning message ** might be generated. */ char *db_get_versioned(const char *zName, char *zNonVersionedSetting){ char *zVersionedSetting = 0; int noWarn = 0; struct _cacheEntry { struct _cacheEntry *next; const char *zName, *zValue; } *cacheEntry = 0; static struct _cacheEntry *cache = 0; |
︙ | ︙ | |||
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 | return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting; } /* ** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the ** repository and local databases. */ char *db_get(const char *zName, char *zDefault){ char *z = 0; | > > > > > < < < < < | < < < | | > > > > > > > > > > > > > > > > | 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 | return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting; } /* ** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the ** repository and local databases. ** ** If no such variable exists, return zDefault. Or, if zName is the name ** of a setting, then the zDefault is ignored and the default value of the ** setting is returned instead. If zName is a versioned setting, then ** versioned value takes priority. */ char *db_get(const char *zName, char *zDefault){ char *z = 0; const Setting *pSetting = db_find_setting(zName, 0); if( g.repositoryOpen ){ z = db_text(0, "SELECT value FROM config WHERE name=%Q", zName); } if( z==0 && g.zConfigDbName ){ db_swap_connections(); z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName); db_swap_connections(); } if( pSetting!=0 && pSetting->versionable ){ /* This is a versionable setting, try and get the info from a ** checked out file */ z = db_get_versioned(zName, z); } if( z==0 ){ if( zDefault==0 && pSetting && pSetting->def[0] ){ z = fossil_strdup(pSetting->def); }else{ z = zDefault; } } return z; } char *db_get_mtime(const char *zName, char *zFormat, char *zDefault){ char *z = 0; if( g.repositoryOpen ){ z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName); } if( z==0 ){ z = zDefault; }else if( zFormat!=0 ){ z = db_text(0, "SELECT strftime(%Q,%Q,'unixepoch');", zFormat, z); } return z; } void db_set(const char *zName, const char *zValue, int globalFlag){ db_begin_transaction(); if( globalFlag ){ db_swap_connections(); |
︙ | ︙ | |||
1891 1892 1893 1894 1895 1896 1897 | int db_lget_int(const char *zName, int dflt){ return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName); } void db_lset_int(const char *zName, int value){ db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); } | < < < < < < < < < < < < < < < < < < < < < > > > > > > > > | < > > > > > > > | | > > > > | < > > > > > | | > | > > > | > > > > > > | 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 | int db_lget_int(const char *zName, int dflt){ return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName); } void db_lset_int(const char *zName, int value){ db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value); } /* ** Record the name of a local repository in the global_config() database. ** The repository filename %s is recorded as an entry with a "name" field ** of the following form: ** ** repo:%s ** ** The value field is set to 1. ** ** If running from a local checkout, also record the root of the checkout ** as follows: ** ** ckout:%s ** ** Where %s is the checkout root. The value is the repository file. */ void db_record_repository_filename(const char *zName){ char *zRepoSetting; char *zCkoutSetting; Blob full; if( zName==0 ){ if( !g.localOpen ) return; zName = db_repository_filename(); } file_canonical_name(zName, &full, 0); (void)filename_collation(); /* Initialize before connection swap */ db_swap_connections(); zRepoSetting = mprintf("repo:%q", blob_str(&full)); db_multi_exec( "DELETE FROM global_config WHERE name %s = %Q;", filename_collation(), zRepoSetting ); db_multi_exec( "INSERT OR IGNORE INTO global_config(name,value)" "VALUES(%Q,1);", zRepoSetting ); fossil_free(zRepoSetting); if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ Blob localRoot; file_canonical_name(g.zLocalRoot, &localRoot, 1); zCkoutSetting = mprintf("ckout:%q", blob_str(&localRoot)); db_multi_exec( "DELETE FROM global_config WHERE name %s = %Q;", filename_collation(), zCkoutSetting ); db_multi_exec( "REPLACE INTO global_config(name, value)" "VALUES(%Q,%Q);", zCkoutSetting, blob_str(&full) ); db_swap_connections(); db_optional_sql("repository", "DELETE FROM config WHERE name %s = %Q;", filename_collation(), zCkoutSetting ); db_optional_sql("repository", "REPLACE INTO config(name,value,mtime)" "VALUES(%Q,1,now());", zCkoutSetting ); fossil_free(zCkoutSetting); blob_reset(&localRoot); }else{ db_swap_connections(); } blob_reset(&full); } /* ** COMMAND: open ** ** Usage: %fossil open FILENAME ?VERSION? ?OPTIONS? ** ** Open a connection to the local repository in FILENAME. A checkout ** for the repository is created with its root at the working directory. ** If VERSION is specified then that version is checked out. Otherwise ** the latest version is checked out. No files other than "manifest" ** and "manifest.uuid" are modified if the --keep option is present. ** ** Options: ** --empty Initialize checkout as being empty, but still connected ** with the local repository. If you commit this checkout, ** it will become a new "initial" commit in the repository. ** --keep Only modify the manifest and manifest.uuid files ** --nested Allow opening a repository inside an opened checkout ** --force-missing Force opening a repository with missing content ** ** See also: close */ void cmd_open(void){ int emptyFlag; int keepFlag; int forceMissingFlag; int allowNested; char **oldArgv; int oldArgc; static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 }; url_proxy_options(); emptyFlag = find_option("empty",0,0)!=0; keepFlag = find_option("keep",0,0)!=0; forceMissingFlag = find_option("force-missing",0,0)!=0; allowNested = find_option("nested",0,0)!=0; /* We should be done with options.. */ verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("REPOSITORY-FILENAME ?VERSION?"); } if( !allowNested && db_open_local(0) ){ fossil_fatal("already within an open tree rooted at %s", g.zLocalRoot); } db_open_repository(g.argv[2]); |
︙ | ︙ | |||
2009 2010 2011 2012 2013 2014 2015 | "COMMIT; PRAGMA journal_mode=WAL; BEGIN;", #endif (char*)0); db_delete_on_failure(LOCALDB_NAME); db_open_local(0); db_lset("repository", g.argv[2]); db_record_repository_filename(g.argv[2]); | < < < | < | | < | | > > > > > > > | | < | > | < < < | | | | | | | > | > > | > > < | | | | > | | > | | > | | | | | | | > | | | > | | | | | > | | | | | > | | | | | | | > | | | | | | | | | | | | > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 | "COMMIT; PRAGMA journal_mode=WAL; BEGIN;", #endif (char*)0); db_delete_on_failure(LOCALDB_NAME); db_open_local(0); db_lset("repository", g.argv[2]); db_record_repository_filename(g.argv[2]); db_lset_int("checkout", 0); oldArgv = g.argv; oldArgc = g.argc; azNewArgv[0] = g.argv[0]; g.argv = azNewArgv; if( !emptyFlag ){ g.argc = 3; if( oldArgc==4 ){ azNewArgv[g.argc-1] = oldArgv[3]; }else if( !db_exists("SELECT 1 FROM event WHERE type='ci'") ){ azNewArgv[g.argc-1] = "--latest"; }else{ azNewArgv[g.argc-1] = db_get("main-branch", "trunk"); } if( keepFlag ){ azNewArgv[g.argc++] = "--keep"; } if( forceMissingFlag ){ azNewArgv[g.argc++] = "--force-missing"; } checkout_cmd(); } g.argc = 2; info_cmd(); } /* ** Print the current value of a setting identified by the pSetting ** pointer. */ static void print_setting(const Setting *pSetting){ Stmt q; if( g.repositoryOpen ){ db_prepare(&q, "SELECT '(local)', value FROM config WHERE name=%Q" " UNION ALL " "SELECT '(global)', value FROM global_config WHERE name=%Q", pSetting->name, pSetting->name ); }else{ db_prepare(&q, "SELECT '(global)', value FROM global_config WHERE name=%Q", pSetting->name ); } if( db_step(&q)==SQLITE_ROW ){ fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0), db_column_text(&q, 1)); }else{ fossil_print("%-20s\n", pSetting->name); } if( pSetting->versionable && g.localOpen ){ /* Check to see if this is overridden by a versionable settings file */ Blob versionedPathname; blob_zero(&versionedPathname); blob_appendf(&versionedPathname, "%s/.fossil-settings/%s", g.zLocalRoot, pSetting->name); if( file_size(blob_str(&versionedPathname))>=0 ){ fossil_print(" (overridden by contents of file .fossil-settings/%s)\n", pSetting->name); } } db_finalize(&q); } #if INTERFACE /* ** Define all settings, which can be controlled via the set/unset ** command. ** ** var is the name of the internal configuration name for db_(un)set. ** If var is 0, the settings name is used. ** ** width is the length for the edit field on the behavior page, 0 ** is used for on/off checkboxes. ** ** The behaviour page doesn't use a special layout. It lists all ** set-commands and displays the 'set'-help as info. */ struct Setting { const char *name; /* Name of the setting */ const char *var; /* Internal variable name used by db_set() */ int width; /* Width of display. 0 for boolean values. */ int versionable; /* Is this setting versionable? */ int forceTextArea; /* Force using a text area for display? */ const char *def; /* Default value */ }; #endif /* INTERFACE */ const Setting aSetting[] = { { "access-log", 0, 0, 0, 0, "off" }, { "admin-log", 0, 0, 0, 0, "off" }, { "allow-symlinks", 0, 0, 1, 0, "off" }, { "auto-captcha", "autocaptcha", 0, 0, 0, "on" }, { "auto-hyperlink", 0, 0, 0, 0, "on", }, { "auto-shun", 0, 0, 0, 0, "on" }, { "autosync", 0, 0, 0, 0, "on" }, { "autosync-tries", 0, 16, 0, 0, "1" }, { "binary-glob", 0, 40, 1, 0, "" }, #if defined(_WIN32) || defined(__CYGWIN__) || defined(__DARWIN__) || \ defined(__APPLE__) { "case-sensitive", 0, 0, 0, 0, "off" }, #else { "case-sensitive", 0, 0, 0, 0, "on" }, #endif { "clean-glob", 0, 40, 1, 0, "" }, { "clearsign", 0, 0, 0, 0, "off" }, { "crnl-glob", 0, 40, 1, 0, "" }, { "default-perms", 0, 16, 0, 0, "u" }, { "diff-binary", 0, 0, 0, 0, "on" }, { "diff-command", 0, 40, 0, 0, "" }, { "dont-push", 0, 0, 0, 0, "off" }, { "dotfiles", 0, 0, 1, 0, "off" }, { "editor", 0, 32, 0, 0, "" }, { "empty-dirs", 0, 40, 1, 0, "" }, { "encoding-glob", 0, 40, 1, 0, "" }, { "gdiff-command", 0, 40, 0, 0, "gdiff" }, { "gmerge-command", 0, 40, 0, 0, "" }, { "hash-digits", 0, 5, 0, 0, "10" }, { "http-port", 0, 16, 0, 0, "8080" }, { "https-login", 0, 0, 0, 0, "off" }, { "ignore-glob", 0, 40, 1, 0, "" }, { "keep-glob", 0, 40, 1, 0, "" }, { "localauth", 0, 0, 0, 0, "off" }, { "main-branch", 0, 40, 0, 0, "trunk" }, { "manifest", 0, 0, 1, 0, "off" }, { "max-loadavg", 0, 25, 0, 0, "0.0" }, { "max-upload", 0, 25, 0, 0, "250000" }, { "mtime-changes", 0, 0, 0, 0, "on" }, { "pgp-command", 0, 40, 0, 0, "gpg --clearsign -o " }, { "proxy", 0, 32, 0, 0, "off" }, { "relative-paths", 0, 0, 0, 0, "on" }, { "repo-cksum", 0, 0, 0, 0, "on" }, { "self-register", 0, 0, 0, 0, "off" }, { "ssh-command", 0, 40, 0, 0, "" }, { "ssl-ca-location", 0, 40, 0, 0, "" }, { "ssl-identity", 0, 40, 0, 0, "" }, #ifdef FOSSIL_ENABLE_TCL { "tcl", 0, 0, 0, 0, "off" }, { "tcl-setup", 0, 40, 1, 1, "" }, #endif #ifdef FOSSIL_ENABLE_TH1_DOCS { "th1-docs", 0, 0, 0, 0, "off" }, #endif #ifdef FOSSIL_ENABLE_TH1_HOOKS { "th1-hooks", 0, 0, 0, 0, "off" }, #endif { "th1-setup", 0, 40, 1, 1, "" }, { "th1-uri-regexp", 0, 40, 1, 0, "" }, { "web-browser", 0, 32, 0, 0, "" }, { 0,0,0,0,0,0 } }; /* ** Look up a control setting by its name. Return a pointer to the Setting ** object, or NULL if there is no such setting. ** ** If allowPrefix is true, then the Setting returned is the first one for ** which zName is a prefix of the Setting name. */ const Setting *db_find_setting(const char *zName, int allowPrefix){ int lwr, mid, upr, c; int n = (int)strlen(zName) + !allowPrefix; lwr = 0; upr = ArraySize(aSetting)-2; while( upr>=lwr ){ mid = (upr+lwr)/2; c = fossil_strncmp(zName, aSetting[mid].name, n); if( c<0 ){ upr = mid - 1; }else if( c>0 ){ lwr = mid + 1; }else{ if( allowPrefix ){ while( mid>lwr && fossil_strncmp(zName, aSetting[mid-1].name, n)==0 ){ mid--; } } return &aSetting[mid]; } } return 0; } /* ** COMMAND: settings ** COMMAND: unset* ** ** %fossil settings ?PROPERTY? ?VALUE? ?OPTIONS? ** %fossil unset PROPERTY ?OPTIONS? |
︙ | ︙ | |||
2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 | ** file exists. ** ** The "unset" command clears a property setting. ** ** ** access-log If enabled, record successful and failed login attempts ** in the "accesslog" table. Default: off ** ** allow-symlinks If enabled, don't follow symlinks, and instead treat ** (versionable) them as symlinks on Unix. Has no effect on Windows ** (existing links in repository created on Unix become ** plain-text files with link destination path inside). ** Default: off ** | > > > | 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 | ** file exists. ** ** The "unset" command clears a property setting. ** ** ** access-log If enabled, record successful and failed login attempts ** in the "accesslog" table. Default: off ** ** admin-log If enabled, record configuration changes in the ** "admin_log" table. Default: off ** ** allow-symlinks If enabled, don't follow symlinks, and instead treat ** (versionable) them as symlinks on Unix. Has no effect on Windows ** (existing links in repository created on Unix become ** plain-text files with link destination path inside). ** Default: off ** |
︙ | ︙ | |||
2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 | ** Default: on ** ** autosync If enabled, automatically pull prior to commit ** or update and automatically push after commit or ** tag or branch creation. If the value is "pullonly" ** then only pull operations occur automatically. ** Default: on ** ** binary-glob The VALUE is a comma or newline-separated list of ** (versionable) GLOB patterns that should be treated as binary files ** for committing and merging purposes. Example: *.jpg ** ** case-sensitive If TRUE, the files whose names differ only in case ** are considered distinct. If FALSE files whose names | > > > > > | 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 | ** Default: on ** ** autosync If enabled, automatically pull prior to commit ** or update and automatically push after commit or ** tag or branch creation. If the value is "pullonly" ** then only pull operations occur automatically. ** Default: on ** ** autosync-tries If autosync is enabled setting this to a value greater ** than zero will cause autosync to try no more than this ** number of attempts if there is a sync failure. ** Default: 1 ** ** binary-glob The VALUE is a comma or newline-separated list of ** (versionable) GLOB patterns that should be treated as binary files ** for committing and merging purposes. Example: *.jpg ** ** case-sensitive If TRUE, the files whose names differ only in case ** are considered distinct. If FALSE files whose names |
︙ | ︙ | |||
2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 | ** external diff programs. If FALSE, skip these files. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** ** editor Text editor command used for check-in comments. ** ** empty-dirs A comma or newline-separated list of pathnames. On ** (versionable) update and checkout commands, if no file or directory ** exists with that name, an empty directory will be ** created. | > > > | 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 | ** external diff programs. If FALSE, skip these files. ** ** diff-command External command to run when performing a diff. ** If undefined, the internal text diff will be used. ** ** dont-push Prevent this repository from pushing from client to ** server. Useful when setting up a private branch. ** ** dotfiles Include --dotfiles option for all compatible commands. ** (versionable) ** ** editor Text editor command used for check-in comments. ** ** empty-dirs A comma or newline-separated list of pathnames. On ** (versionable) update and checkout commands, if no file or directory ** exists with that name, an empty directory will be ** created. |
︙ | ︙ | |||
2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 | ** diff. If undefined, text diff will be used. ** ** gmerge-command A graphical merge conflict resolver command operating ** on four files. ** Ex: kdiff3 "%baseline" "%original" "%merge" -o "%output" ** Ex: xxdiff "%original" "%baseline" "%merge" -M "%output" ** Ex: meld "%baseline" "%original" "%merge" "%output" ** ** http-port The TCP/IP port number to use by the "server" ** and "ui" commands. Default: 8080 ** ** https-login Send login credentials using HTTPS instead of HTTP ** even if the login page request came via HTTP. ** | > > > | 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 | ** diff. If undefined, text diff will be used. ** ** gmerge-command A graphical merge conflict resolver command operating ** on four files. ** Ex: kdiff3 "%baseline" "%original" "%merge" -o "%output" ** Ex: xxdiff "%original" "%baseline" "%merge" -M "%output" ** Ex: meld "%baseline" "%original" "%merge" "%output" ** ** hash-digits The number of hexadecimal digits of the SHA1 hash to ** display. (Default: 10; Minimum: 6) ** ** http-port The TCP/IP port number to use by the "server" ** and "ui" commands. Default: 8080 ** ** https-login Send login credentials using HTTPS instead of HTTP ** even if the login page request came via HTTP. ** |
︙ | ︙ | |||
2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 | ** unrestricted access to the repository. ** ** main-branch The primary branch for the project. Default: trunk ** ** manifest If enabled, automatically create files "manifest" and ** (versionable) "manifest.uuid" in every checkout. The SQLite and ** Fossil repositories both require this. Default: off. ** ** max-upload A limit on the size of uplink HTTP requests. The ** default is 250000 bytes. ** ** mtime-changes Use file modification times (mtimes) to detect when ** files have been modified. (Default "on".) ** | > > > > > > > | 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 | ** unrestricted access to the repository. ** ** main-branch The primary branch for the project. Default: trunk ** ** manifest If enabled, automatically create files "manifest" and ** (versionable) "manifest.uuid" in every checkout. The SQLite and ** Fossil repositories both require this. Default: off. ** ** max-loadavg Some CPU-intensive web pages (ex: /zip, /tarball, /blame) ** are disallowed if the system load average goes above this ** value. "0.0" means no limit. This only works on unix. ** Only local settings of this value make a difference since ** when running as a web-server, Fossil does not open the ** global configuration database. ** ** max-upload A limit on the size of uplink HTTP requests. The ** default is 250000 bytes. ** ** mtime-changes Use file modification times (mtimes) to detect when ** files have been modified. (Default "on".) ** |
︙ | ︙ | |||
2331 2332 2333 2334 2335 2336 2337 | ** Tcl integration commands will be added to the TH1 ** interpreter, allowing arbitrary Tcl expressions and ** scripts to be evaluated from TH1. Additionally, the Tcl ** interpreter will be able to evaluate arbitrary TH1 ** expressions and scripts. Default: off. ** ** tcl-setup This is the setup script to be evaluated after creating | | > > > > > > > > > > > > > > | > > > > | 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 | ** Tcl integration commands will be added to the TH1 ** interpreter, allowing arbitrary Tcl expressions and ** scripts to be evaluated from TH1. Additionally, the Tcl ** interpreter will be able to evaluate arbitrary TH1 ** expressions and scripts. Default: off. ** ** tcl-setup This is the setup script to be evaluated after creating ** (versionable) and initializing the Tcl interpreter. By default, this ** is empty and no extra setup is performed. ** ** th1-docs WARNING: If enabled (and Fossil was compiled with TH1 ** support for embedded documentation files), this allows ** embedded documentation files to contain arbitrary TH1 ** scripts that are evaluated on the server. If native ** Tcl integration is also enabled, this setting has the ** potential to allow anybody with check-in privileges to ** do almost anything that the associated operating system ** user account could do. Extreme caution should be used ** when enabling this setting. Default: off. ** ** th1-hooks If enabled (and Fossil was compiled with support for TH1 ** hooks), special TH1 commands will be called before and ** after any Fossil command or web page. Default: off. ** ** th1-setup This is the setup script to be evaluated after creating ** (versionable) and initializing the TH1 interpreter. By default, this ** is empty and no extra setup is performed. ** ** th1-uri-regexp Specify which URI's are allowed in HTTP requests from ** (versionable) TH1 scripts. If empty, no HTTP requests are allowed ** whatsoever. The default is an empty string. ** ** web-browser A shell command used to launch your preferred ** web browser when given a URL as an argument. ** Defaults to "start" on windows, "open" on Mac, ** and "firefox" on Unix. ** ** Options: |
︙ | ︙ | |||
2363 2364 2365 2366 2367 2368 2369 2370 | } if( !g.repositoryOpen ){ globalFlag = 1; } if( unsetFlag && g.argc!=3 ){ usage("PROPERTY ?-global?"); } if( g.argc==2 ){ | > > > > > > > > > > > > < | | < | > | | | | > | > > > > > > > > > > > > | | | | | < < | < | | | | > > > > > > | 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 | } if( !g.repositoryOpen ){ globalFlag = 1; } if( unsetFlag && g.argc!=3 ){ usage("PROPERTY ?-global?"); } /* Verify that the aSetting[] entries are in sorted order. This is ** necessary for the binary search in db_find_setting() to work correctly. */ for(i=1; aSetting[i].name; i++){ if( fossil_strcmp(aSetting[i-1].name, aSetting[i].name)>=0 ){ fossil_panic("Internal Error: aSetting[] entries for \"%s\"" " and \"%s\" are out of order.", aSetting[i-1].name, aSetting[i].name); } } if( g.argc==2 ){ for(i=0; aSetting[i].name; i++){ print_setting(&aSetting[i]); } }else if( g.argc==3 || g.argc==4 ){ const char *zName = g.argv[2]; int n = (int)strlen(zName); const Setting *pSetting = db_find_setting(zName, 1); if( pSetting==0 ){ fossil_fatal("no such setting: %s", zName); } if( globalFlag && fossil_strcmp(pSetting->name, "manifest")==0 ){ fossil_fatal("cannot set 'manifest' globally"); } if( unsetFlag || g.argc==4 ){ int isManifest = fossil_strcmp(pSetting->name, "manifest")==0; if( n!=strlen(pSetting[0].name) && pSetting[1].name && fossil_strncmp(pSetting[1].name, zName, n)==0 ){ Blob x; int i; blob_init(&x,0,0); for(i=0; pSetting[i].name; i++){ if( fossil_strncmp(pSetting[i].name,zName,n)!=0 ) break; blob_appendf(&x, " %s", pSetting[i].name); } fossil_fatal("ambiguous setting \"%s\" - might be:%s", zName, blob_str(&x)); } if( globalFlag && isManifest ){ fossil_fatal("cannot set 'manifest' globally"); } if( unsetFlag ){ db_unset(pSetting->name, globalFlag); }else{ db_set(pSetting->name, g.argv[3], globalFlag); } if( isManifest && g.localOpen ){ manifest_to_disk(db_lget_int("checkout", 0)); } }else{ while( pSetting->name && fossil_strncmp(pSetting->name,zName,n)==0 ){ print_setting(pSetting); pSetting++; } } }else{ usage("?PROPERTY? ?VALUE? ?-global?"); } } /* |
︙ | ︙ | |||
2440 2441 2442 2443 2444 2445 2446 | if( g.argc!=3 ) usage("TIMESTAMP"); sqlite3_open(":memory:", &g.db); rDiff = db_double(0.0, "SELECT julianday('now') - julianday(%Q)", g.argv[2]); fossil_print("Time differences: %s\n", db_timespan_name(rDiff)); sqlite3_close(g.db); g.db = 0; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 | if( g.argc!=3 ) usage("TIMESTAMP"); sqlite3_open(":memory:", &g.db); rDiff = db_double(0.0, "SELECT julianday('now') - julianday(%Q)", g.argv[2]); fossil_print("Time differences: %s\n", db_timespan_name(rDiff)); sqlite3_close(g.db); g.db = 0; } /* ** COMMAND: test-without-rowid ** %fossil test-without-rowid FILENAME... ** ** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID ** optimization. FILENAME can also be the ~/.fossil file or a local ** .fslckout or _FOSSIL_ file. ** ** The purpose of this command is for testing the WITHOUT ROWID capabilities ** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil. ** ** Options: ** --dryrun | -n No changes. Just print what would happen. */ void test_without_rowid(void){ int i, j; Stmt q; Blob allSql; int dryRun = find_option("dry-run", "n", 0)!=0; for(i=2; i<g.argc; i++){ db_open_or_attach(g.argv[i], "main", 0); blob_init(&allSql, "BEGIN;\n", -1); db_prepare(&q, "SELECT name, sql FROM main.sqlite_master " " WHERE type='table' AND sql NOT LIKE '%%WITHOUT ROWID%%'" " AND name IN ('global_config','shun','concealed','config'," " 'plink','tagxref','backlink','vcache');" ); while( db_step(&q)==SQLITE_ROW ){ const char *zTName = db_column_text(&q, 0); const char *zOrigSql = db_column_text(&q, 1); Blob newSql; blob_init(&newSql, 0, 0); for(j=0; zOrigSql[j]; j++){ if( fossil_strnicmp(zOrigSql+j,"unique",6)==0 ){ blob_append(&newSql, zOrigSql, j); blob_append(&newSql, "PRIMARY KEY", -1); zOrigSql += j+6; j = -1; } } blob_append(&newSql, zOrigSql, -1); blob_append_sql(&allSql, "ALTER TABLE \"%w\" RENAME TO \"x_%w\";\n" "%s WITHOUT ROWID;\n" "INSERT INTO \"%w\" SELECT * FROM \"x_%w\";\n" "DROP TABLE \"x_%w\";\n", zTName, zTName, blob_sql_text(&newSql), zTName, zTName, zTName ); fossil_print("Converting table %s of %s to WITHOUT ROWID.\n", zTName, g.argv[i]); blob_reset(&newSql); } blob_append_sql(&allSql, "COMMIT;\n"); db_finalize(&q); if( dryRun ){ fossil_print("SQL that would have been evaluated:\n"); fossil_print("%.78c\n", '-'); fossil_print("%s", blob_sql_text(&allSql)); }else{ db_multi_exec("%s", blob_sql_text(&allSql)); } blob_reset(&allSql); db_close(1); } } /* ** Make sure the adminlog table exists. Create it if it does not */ void create_admin_log_table(void){ static int once = 0; if( once ) return; once = 1; db_multi_exec( "CREATE TABLE IF NOT EXISTS \"%w\".admin_log(\n" " id INTEGER PRIMARY KEY,\n" " time INTEGER, -- Seconds since 1970\n" " page TEXT, -- path of page\n" " who TEXT, -- User who made the change\n " " what TEXT -- What changed\n" ")", db_name("repository") ); } /* ** Write a message into the admin_event table, if admin logging is ** enabled via the admin-log configuration option. */ void admin_log(const char *zFormat, ...){ Blob what = empty_blob; va_list ap; if( !db_get_boolean("admin-log", 0) ){ /* Potential leak here (on %z params) but the alternative is to let blob_vappendf() do it below. */ return; } create_admin_log_table(); va_start(ap,zFormat); blob_vappendf( &what, zFormat, ap ); va_end(ap); db_multi_exec("INSERT INTO admin_log(time,page,who,what)" " VALUES(now(), %Q, %Q, %B)", g.zPath, g.zLogin, &what); blob_reset(&what); } |
Changes to src/delta.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ** This module implements the delta compress algorithm. ** ** Though developed specifically for fossil, the code in this file ** is generally applicable and is thus easily separated from the ** fossil source code base. Nothing in this file depends on anything ** else in fossil. */ #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <string.h> #include "delta.h" /* | > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ** This module implements the delta compress algorithm. ** ** Though developed specifically for fossil, the code in this file ** is generally applicable and is thus easily separated from the ** fossil source code base. Nothing in this file depends on anything ** else in fossil. */ #include "config.h" #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <string.h> #include "delta.h" /* |
︙ | ︙ | |||
62 63 64 65 66 67 68 | #if INTERFACE /* ** The "u32" type must be an unsigned 32-bit integer. Adjust this */ typedef unsigned int u32; /* | | | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | #if INTERFACE /* ** The "u32" type must be an unsigned 32-bit integer. Adjust this */ typedef unsigned int u32; /* ** Must be a 16-bit value */ typedef short int s16; typedef unsigned short int u16; #endif /* INTERFACE */ /* ** The width of a hash window in bytes. The algorithm only works if this ** is a power of 2. */ #define NHASH 16 /* ** The current state of the rolling hash. ** ** z[] holds the values that have been hashed. z[] is a circular buffer. ** z[i] is the first entry and z[(i+NHASH-1)%NHASH] is the last entry of ** the window. ** ** Hash.a is the sum of all elements of hash.z[]. Hash.b is a weighted ** sum. Hash.b is z[i]*NHASH + z[i+1]*(NHASH-1) + ... + z[i+NHASH-1]*1. ** (Each index for z[] should be module NHASH, of course. The %NHASH operator ** is omitted in the prior expression for brevity.) */ |
︙ | ︙ | |||
132 133 134 135 136 137 138 | return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16); } /* ** Write an base-64 integer into the given buffer. */ static void putInt(unsigned int v, char **pz){ | | | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16); } /* ** Write an base-64 integer into the given buffer. */ static void putInt(unsigned int v, char **pz){ static const char zDigits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"; /* 123456789 123456789 123456789 123456789 123456789 123456789 123 */ int i, j; char zBuf[20]; if( v==0 ){ *(*pz)++ = '0'; return; |
︙ | ︙ | |||
226 227 228 229 230 231 232 | } return sum3; } /* ** Create a new delta. ** | | | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | } return sum3; } /* ** Create a new delta. ** ** The delta is written into a preallocated buffer, zDelta, which ** should be at least 60 bytes longer than the target file, zOut. ** The delta string will be NUL-terminated, but it might also contain ** embedded NUL characters if either the zSrc or zOut files are ** binary. This function returns the length of the delta string ** in bytes, excluding the final NUL terminator character. ** ** Output Format: ** ** The delta begins with a base64 number followed by a newline. This ** number is the number of bytes in the TARGET file. Thus, given a ** delta file z, a program can compute the size of the output file ** simply by reading the first line and decoding the base-64 number ** found there. The delta_output_size() routine does exactly this. ** ** After the initial size number, the delta consists of a series of ** literal text segments and commands to copy from the SOURCE file. ** A copy command looks like this: ** ** NNN@MMM, ** ** where NNN is the number of bytes to be copied and MMM is the offset ** into the source file of the first byte (both base-64). If NNN is 0 ** it means copy the rest of the input file. Literal text is like this: |
︙ | ︙ | |||
280 281 282 283 284 285 286 | ** Next we begin scanning the target file using a sliding 16-byte ** window. The hash of the 16-byte window in the target is used to ** search for a matching section in the source file. When a match ** is found, a copy command is added to the delta. An effort is ** made to extend the matching section to regions that come before ** and after the 16-byte hash window. A copy command is only issued ** if the result would use less space that just quoting the text | | | 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | ** Next we begin scanning the target file using a sliding 16-byte ** window. The hash of the 16-byte window in the target is used to ** search for a matching section in the source file. When a match ** is found, a copy command is added to the delta. An effort is ** made to extend the matching section to regions that come before ** and after the 16-byte hash window. A copy command is only issued ** if the result would use less space that just quoting the text ** literally. Literal text is added to the delta for sections that ** do not match or which can not be encoded efficiently using copy ** commands. */ int delta_create( const char *zSrc, /* The source or pattern file */ unsigned int lenSrc, /* Length of the source file */ const char *zOut, /* The target file */ |
︙ | ︙ | |||
353 354 355 356 357 358 359 | int limit = 250; hv = hash_32bit(&h) % nHash; DEBUG2( printf("LOOKING: %4d [%s]\n", base+i, print16(&zOut[base+i])); ) iBlock = landmark[hv]; while( iBlock>=0 && (limit--)>0 ){ /* | | | | 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | int limit = 250; hv = hash_32bit(&h) % nHash; DEBUG2( printf("LOOKING: %4d [%s]\n", base+i, print16(&zOut[base+i])); ) iBlock = landmark[hv]; while( iBlock>=0 && (limit--)>0 ){ /* ** The hash window has identified a potential match against ** landmark block iBlock. But we need to investigate further. ** ** Look for a region in zOut that matches zSrc. Anchor the search ** at zSrc[iSrc] and zOut[base+i]. Do not include anything prior to ** zOut[base] or after zOut[outLen] nor anything after zSrc[srcLen]. ** ** Set cnt equal to the length of the match and set ofst so that ** zSrc[ofst] is the first element of the match. litsz is the number ** of characters between zOut[base] and the beginning of the match. |
︙ | ︙ | |||
464 465 466 467 468 469 470 | *(zDelta++) = ':'; memcpy(zDelta, &zOut[base], lenOut-base); zDelta += lenOut-base; } /* Output the final checksum record. */ putInt(checksum(zOut, lenOut), &zDelta); *(zDelta++) = ';'; | | | | | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 | *(zDelta++) = ':'; memcpy(zDelta, &zOut[base], lenOut-base); zDelta += lenOut-base; } /* Output the final checksum record. */ putInt(checksum(zOut, lenOut), &zDelta); *(zDelta++) = ';'; fossil_free(collide); return zDelta - zOrigDelta; } /* ** Return the size (in bytes) of the output from applying ** a delta. ** ** This routine is provided so that an procedure that is able ** to call delta_apply() can learn how much space is required ** for the output and hence allocate nor more space that is really ** needed. */ int delta_output_size(const char *zDelta, int lenDelta){ |
︙ | ︙ |
Changes to src/deltacmd.c.
︙ | ︙ | |||
15 16 17 18 19 20 21 | ** ******************************************************************************* ** ** This module implements the interface to the delta generator. */ #include "config.h" #include "deltacmd.h" | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | ** ******************************************************************************* ** ** This module implements the interface to the delta generator. */ #include "config.h" #include "deltacmd.h" /* ** Create a delta that describes the change from pOriginal to pTarget ** and put that delta in pDelta. The pDelta blob is assumed to be ** uninitialized. */ int blob_delta_create(Blob *pOriginal, Blob *pTarget, Blob *pDelta){ const char *zOrig, *zTarg; |
︙ | ︙ |
Changes to src/descendants.c.
︙ | ︙ | |||
28 29 30 31 32 33 34 | ** already exist. Load this table with the RID of all ** check-ins that are leaves which are descended from ** check-in iBase. ** ** A "leaf" is a check-in that has no children in the same branch. ** There is a separate permanent table LEAF that contains all leaves ** in the tree. This routine is used to compute a subset of that | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | ** already exist. Load this table with the RID of all ** check-ins that are leaves which are descended from ** check-in iBase. ** ** A "leaf" is a check-in that has no children in the same branch. ** There is a separate permanent table LEAF that contains all leaves ** in the tree. This routine is used to compute a subset of that ** table consisting of leaves that are descended from a single check-in. ** ** The closeMode flag determines behavior associated with the "closed" ** tag: ** ** closeMode==0 Show all leaves regardless of the "closed" tag. ** ** closeMode==1 Show only leaves without the "closed" tag. |
︙ | ︙ | |||
128 129 130 131 132 133 134 135 136 137 138 139 140 141 | } } db_finalize(&ins); db_finalize(&isBr); db_finalize(&q1); bag_clear(&pending); bag_clear(&seen); } if( closeMode==1 ){ db_multi_exec( "DELETE FROM leaves WHERE rid IN" " (SELECT leaves.rid FROM leaves, tagxref" " WHERE tagxref.rid=leaves.rid " " AND tagxref.tagid=%d" | > > > > > | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | } } db_finalize(&ins); db_finalize(&isBr); db_finalize(&q1); bag_clear(&pending); bag_clear(&seen); }else{ db_multi_exec( "INSERT INTO leaves" " SELECT leaf.rid FROM leaf" ); } if( closeMode==1 ){ db_multi_exec( "DELETE FROM leaves WHERE rid IN" " (SELECT leaves.rid FROM leaves, tagxref" " WHERE tagxref.rid=leaves.rid " " AND tagxref.tagid=%d" |
︙ | ︙ | |||
155 156 157 158 159 160 161 | } /* ** Load the record ID rid and up to N-1 closest ancestors into ** the "ok" table. */ void compute_ancestors(int rid, int N, int directOnly){ | | | | > | < | | | > > > | < < | | < < < < < < < < < < < < < < < < < < | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | } /* ** Load the record ID rid and up to N-1 closest ancestors into ** the "ok" table. */ void compute_ancestors(int rid, int N, int directOnly){ db_multi_exec( "WITH RECURSIVE " " ancestor(rid, mtime) AS (" " SELECT %d, mtime FROM event WHERE objid=%d " " UNION " " SELECT plink.pid, event.mtime" " FROM ancestor, plink, event" " WHERE plink.cid=ancestor.rid" " AND event.objid=plink.pid %s" " ORDER BY mtime DESC LIMIT %d" " )" "INSERT INTO ok" " SELECT rid FROM ancestor;", rid, rid, directOnly ? "AND plink.isPrim" : "", N ); } /* ** Compute up to N direct ancestors (merge ancestors do not count) ** for the check-in rid and put them in a table named "ancestor". ** Label each generation with consecutive integers going backwards ** in time such that rid has the smallest generation number and the oldest |
︙ | ︙ | |||
299 300 301 302 303 304 305 | db_finalize(&ins); db_finalize(&q); } /* ** COMMAND: descendants* ** | | | | > > > | > > > > > > > > > > > > > > | | | | | > > > > | > > > > > > > > > > > > | | | | | | | | > > | | | | | < < < < < < | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 | db_finalize(&ins); db_finalize(&q); } /* ** COMMAND: descendants* ** ** Usage: %fossil descendants ?CHECKIN? ?OPTIONS? ** ** Find all leaf descendants of the check-in specified or if the argument ** is omitted, of the check-in currently checked out. ** ** Options: ** -R|--repository FILE Extract info from repository FILE ** -W|--width <num> Width of lines (default is to auto-detect). ** Must be >20 or 0 (= no limit, resulting in a ** single line per entry). ** ** See also: finfo, info, leaves */ void descendants_cmd(void){ Stmt q; int base, width; const char *zWidth; db_find_and_open_repository(0,0); zWidth = find_option("width","W",1); if( zWidth ){ width = atoi(zWidth); if( (width!=0) && (width<=20) ){ fossil_fatal("-W|--width value must be >20 or 0"); } }else{ width = -1; } /* We should be done with options.. */ verify_all_options(); if( g.argc==2 ){ base = db_lget_int("checkout", 0); }else{ base = name_to_typed_rid(g.argv[2], "ci"); } if( base==0 ) return; compute_leaves(base, 0); db_prepare(&q, "%s" " AND event.objid IN (SELECT rid FROM leaves)" " ORDER BY event.mtime DESC", timeline_query_for_tty() ); print_timeline(&q, 0, width, 0); db_finalize(&q); } /* ** COMMAND: leaves* ** ** Usage: %fossil leaves ?OPTIONS? ** ** Find leaves of all branches. By default show only open leaves. ** The -a|--all flag causes all leaves (closed and open) to be shown. ** The -c|--closed flag shows only closed leaves. ** ** The --recompute flag causes the content of the "leaf" table in the ** repository database to be recomputed. ** ** Options: ** -a|--all show ALL leaves ** -c|--closed show only closed leaves ** --bybranch order output by branch name ** --recompute recompute the "leaf" table in the repository DB ** -W|--width <num> Width of lines (default is to auto-detect). Must be ** >39 or 0 (= no limit, resulting in a single line per ** entry). ** ** See also: descendants, finfo, info, branch */ void leaves_cmd(void){ Stmt q; Blob sql; int showAll = find_option("all", "a", 0)!=0; int showClosed = find_option("closed", "c", 0)!=0; int recomputeFlag = find_option("recompute",0,0)!=0; int byBranch = find_option("bybranch",0,0)!=0; const char *zWidth = find_option("width","W",1); char *zLastBr = 0; int n, width; char zLineNo[10]; if( zWidth ){ width = atoi(zWidth); if( (width!=0) && (width<=39) ){ fossil_fatal("-W|--width value must be >39 or 0"); } }else{ width = -1; } db_find_and_open_repository(0,0); /* We should be done with options.. */ verify_all_options(); if( recomputeFlag ) leaf_rebuild(); blob_zero(&sql); blob_append(&sql, timeline_query_for_tty(), -1); blob_append_sql(&sql, " AND blob.rid IN leaf"); if( showClosed ){ blob_append_sql(&sql," AND %z", leaf_is_closed_sql("blob.rid")); }else if( !showAll ){ blob_append_sql(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid")); } if( byBranch ){ db_prepare(&q, "%s ORDER BY nullif(branch,'trunk') COLLATE nocase," " event.mtime DESC", blob_sql_text(&sql)); }else{ db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql)); } blob_reset(&sql); n = 0; while( db_step(&q)==SQLITE_ROW ){ const char *zId = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); const char *zCom = db_column_text(&q, 3); const char *zBr = db_column_text(&q, 7); char *z; if( byBranch && fossil_strcmp(zBr, zLastBr)!=0 ){ fossil_print("*** %s ***\n", zBr); fossil_free(zLastBr); zLastBr = fossil_strdup(zBr); } n++; sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n); fossil_print("%6s ", zLineNo); z = mprintf("%s [%S] %s", zDate, zId, zCom); comment_print(z, zCom, 7, width, g.comFmtFlags); fossil_free(z); } fossil_free(zLastBr); db_finalize(&q); } /* ** WEBPAGE: leaves ** ** Find leaves of all branches. */ void leaves_page(void){ Blob sql; Stmt q; int showAll = P("all")!=0; int showClosed = P("closed")!=0; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( !showAll ){ style_submenu_element("All", "All", "leaves?all"); } if( !showClosed ){ style_submenu_element("Closed", "Closed", "leaves?closed"); } if( showClosed || showAll ){ style_submenu_element("Open", "Open", "leaves"); } style_header("Leaves"); login_anonymous_available(); #if 0 style_sidebox_begin("Nomenclature:", "33%"); @ <ol> @ <li> A <div class="sideboxDescribed">leaf</div> @ is a check-in with no descendants in the same branch.</li> @ <li> An <div class="sideboxDescribed">open leaf</div> @ is a leaf that does not have a "closed" tag @ and is thus assumed to still be in use.</li> @ <li> A <div class="sideboxDescribed">closed leaf</div> @ has a "closed" tag and is thus assumed to @ be historical and no longer in active use.</li> @ </ol> style_sidebox_end(); #endif if( showAll ){ @ <h1>All leaves, both open and closed:</h1> }else if( showClosed ){ @ <h1>Closed leaves:</h1> }else{ @ <h1>Open leaves:</h1> } blob_zero(&sql); blob_append(&sql, timeline_query_for_www(), -1); blob_append_sql(&sql, " AND blob.rid IN leaf"); if( showClosed ){ blob_append_sql(&sql," AND %z", leaf_is_closed_sql("blob.rid")); }else if( !showAll ){ blob_append_sql(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid")); } db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql)); blob_reset(&sql); www_print_timeline(&q, TIMELINE_LEAFONLY, 0, 0, 0, 0); db_finalize(&q); @ <br /> style_footer(); } #if INTERFACE /* Flag parameters to compute_uses_file() */ #define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */ |
︙ | ︙ | |||
491 492 493 494 495 496 497 | Bag pending; Stmt ins; Stmt q; int rid; bag_init(&seen); bag_init(&pending); | | | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 | Bag pending; Stmt ins; Stmt q; int rid; bag_init(&seen); bag_init(&pending); db_prepare(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES(:rid)", zTab); db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid); while( db_step(&q)==SQLITE_ROW ){ int mid = db_column_int(&q, 0); bag_insert(&pending, mid); bag_insert(&seen, mid); db_bind_int(&ins, ":rid", mid); db_step(&ins); |
︙ | ︙ | |||
514 515 516 517 518 519 520 | if( usesFlags & USESFILE_DELETE ){ db_bind_int(&ins, ":rid", mid); db_step(&ins); db_reset(&ins); } } db_finalize(&q); | | | 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 | if( usesFlags & USESFILE_DELETE ){ db_bind_int(&ins, ":rid", mid); db_step(&ins); db_reset(&ins); } } db_finalize(&q); db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid AND isprim"); while( (rid = bag_first(&pending))!=0 ){ bag_remove(&pending, rid); db_bind_int(&q, ":rid", rid); while( db_step(&q)==SQLITE_ROW ){ int mid = db_column_int(&q, 0); if( bag_find(&seen, mid) ) continue; |
︙ | ︙ |
Changes to src/diff.c.
︙ | ︙ | |||
27 28 29 30 31 32 33 | /* ** Flag parameters to the text_diff() routine used to control the formatting ** of the diff output. */ #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */ #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */ #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */ | > | | | < | | < > > > > > > | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | /* ** Flag parameters to the text_diff() routine used to control the formatting ** of the diff output. */ #define DIFF_CONTEXT_MASK ((u64)0x0000ffff) /* Lines of context. Default if 0 */ #define DIFF_WIDTH_MASK ((u64)0x00ff0000) /* side-by-side column width */ #define DIFF_IGNORE_EOLWS ((u64)0x01000000) /* Ignore end-of-line whitespace */ #define DIFF_IGNORE_ALLWS ((u64)0x03000000) /* Ignore all whitespace */ #define DIFF_SIDEBYSIDE ((u64)0x04000000) /* Generate a side-by-side diff */ #define DIFF_VERBOSE ((u64)0x08000000) /* Missing shown as empty files */ #define DIFF_BRIEF ((u64)0x10000000) /* Show filenames only */ #define DIFF_HTML ((u64)0x20000000) /* Render for HTML */ #define DIFF_LINENO ((u64)0x40000000) /* Show line numbers */ #define DIFF_NOOPT (((u64)0x01)<<32) /* Suppress optimizations (debug) */ #define DIFF_INVERT (((u64)0x02)<<32) /* Invert the diff (debug) */ #define DIFF_CONTEXT_EX (((u64)0x04)<<32) /* Use context even if zero */ #define DIFF_NOTTOOBIG (((u64)0x08)<<32) /* Only display if not too big */ #define DIFF_STRIP_EOLCR (((u64)0x10)<<32) /* Strip trailing CR */ /* ** These error messages are shared in multiple locations. They are defined ** here for consistency. */ #define DIFF_CANNOT_COMPUTE_BINARY \ "cannot compute difference between binary files\n" #define DIFF_CANNOT_COMPUTE_SYMLINK \ "cannot compute difference between symlink and regular file\n" #define DIFF_TOO_MANY_CHANGES \ "more than 10,000 changes\n" #define DIFF_WHITESPACE_ONLY \ "whitespace changes only\n" /* ** Maximum length of a line in a text file, in bytes. (2**13 = 8192 bytes) */ #define LENGTH_MASK_SZ 13 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1) #endif /* INTERFACE */ /* ** Information about each line of a file being diffed. ** ** The lower LENGTH_MASK_SZ bits of the hash (DLine.h) are the length ** of the line. If any line is longer than LENGTH_MASK characters, ** the file is considered binary. */ typedef struct DLine DLine; struct DLine { const char *z; /* The text of the line */ unsigned int h; /* Hash of the line */ unsigned short indent; /* Indent of the line. Only !=0 with -w/-Z option */ unsigned short n; /* number of bytes */ unsigned int iNext; /* 1+(Index of next line with same the same hash) */ /* an array of DLine elements serves two purposes. The fields ** above are one per line of input text. But each entry is also ** a bucket in a hash table, as follows: */ unsigned int iHash; /* 1+(first entry in the hash chain) */ }; /* ** Length of a dline */ #define LENGTH(X) ((X)->n) /* ** A context for running a raw diff. ** ** The aEdit[] array describes the raw diff. Each triple of integers in ** aEdit[] means: ** |
︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | int *aEdit; /* Array of copy/delete/insert triples */ int nEdit; /* Number of integers (3x num of triples) in aEdit[] */ int nEditAlloc; /* Space allocated for aEdit[] */ DLine *aFrom; /* File on left side of the diff */ int nFrom; /* Number of lines in aFrom[] */ DLine *aTo; /* File on right side of the diff */ int nTo; /* Number of lines in aTo[] */ }; /* ** Return an array of DLine objects containing a pointer to the ** start of each line and a hash of that line. The lower ** bits of the hash store the length of each line. ** ** Trailing whitespace is removed from each line. 2010-08-20: Not any ** more. If trailing whitespace is ignored, the "patch" command gets ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b] ** ** Return 0 if the file is binary or contains a line that is ** too long. ** ** Profiling show that in most cases this routine consumes the bulk of ** the CPU time on a diff. */ | > | | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | int *aEdit; /* Array of copy/delete/insert triples */ int nEdit; /* Number of integers (3x num of triples) in aEdit[] */ int nEditAlloc; /* Space allocated for aEdit[] */ DLine *aFrom; /* File on left side of the diff */ int nFrom; /* Number of lines in aFrom[] */ DLine *aTo; /* File on right side of the diff */ int nTo; /* Number of lines in aTo[] */ int (*same_fn)(const DLine *, const DLine *); /* Function to be used for comparing */ }; /* ** Return an array of DLine objects containing a pointer to the ** start of each line and a hash of that line. The lower ** bits of the hash store the length of each line. ** ** Trailing whitespace is removed from each line. 2010-08-20: Not any ** more. If trailing whitespace is ignored, the "patch" command gets ** confused by the diff output. Ticket [a9f7b23c2e376af5b0e5b] ** ** Return 0 if the file is binary or contains a line that is ** too long. ** ** Profiling show that in most cases this routine consumes the bulk of ** the CPU time on a diff. */ static DLine *break_into_lines(const char *z, int n, int *pnLine, u64 diffFlags){ int nLine, i, j, k, s, x; unsigned int h, h2; DLine *a; /* Count the number of lines. Allocate space to hold ** the returned array. */ for(i=j=0, nLine=1; i<n; i++, j++){ |
︙ | ︙ | |||
156 157 158 159 160 161 162 | if( n==0 ){ *pnLine = 0; return a; } /* Fill in the array */ for(i=0; i<nLine; i++){ | < > > > > > > > | > > > > | > > > | | > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 | if( n==0 ){ *pnLine = 0; return a; } /* Fill in the array */ for(i=0; i<nLine; i++){ for(j=0; z[j] && z[j]!='\n'; j++){} a[i].z = z; k = j; if( diffFlags & DIFF_STRIP_EOLCR ){ if( k>0 && z[k-1]=='\r' ){ k--; } } a[i].n = k; s = 0; if( diffFlags & DIFF_IGNORE_EOLWS ){ while( k>0 && fossil_isspace(z[k-1]) ){ k--; } } if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){ int numws = 0; while( s<k && fossil_isspace(z[s]) ){ s++; } for(h=0, x=s; x<k; x++){ if( fossil_isspace(z[x]) ){ ++numws; }else{ h = h ^ (h<<2) ^ z[x]; } } k -= numws; }else{ for(h=0, x=s; x<k; x++){ h = h ^ (h<<2) ^ z[x]; } } a[i].indent = s; a[i].h = h = (h<<LENGTH_MASK_SZ) | (k-s); h2 = h % nLine; a[i].iNext = a[h2].iHash; a[h2].iHash = i+1; z += j+1; } /* Return results */ *pnLine = nLine; return a; } /* ** Return true if two DLine elements are identical. */ static int same_dline(const DLine *pA, const DLine *pB){ return pA->h==pB->h && memcmp(pA->z,pB->z, pA->h&LENGTH_MASK)==0; } /* ** Return true if two DLine elements are identical, ignoring ** all whitespace. The indent field of pA/pB already points ** to the first non-space character in the string. */ static int same_dline_ignore_allws(const DLine *pA, const DLine *pB){ int a = pA->indent, b = pB->indent; if( pA->h==pB->h ){ while( a<pA->n || b<pB->n ){ if( a<pA->n && b<pB->n && pA->z[a++] != pB->z[b++] ) return 0; while( a<pA->n && fossil_isspace(pA->z[a])) ++a; while( b<pB->n && fossil_isspace(pB->z[b])) ++b; } return pA->n-a == pB->n-b; } return 0; } /* ** Return true if the regular expression *pRe matches any of the ** N dlines */ static int re_dline_match( |
︙ | ︙ | |||
219 220 221 222 223 224 225 | if( pRe && re_dline_match(pRe, pLine, 1)==0 ){ cPrefix = ' '; }else if( cPrefix=='+' ){ blob_append(pOut, "<span class=\"diffadd\">", -1); }else if( cPrefix=='-' ){ blob_append(pOut, "<span class=\"diffrm\">", -1); } | | | | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | if( pRe && re_dline_match(pRe, pLine, 1)==0 ){ cPrefix = ' '; }else if( cPrefix=='+' ){ blob_append(pOut, "<span class=\"diffadd\">", -1); }else if( cPrefix=='-' ){ blob_append(pOut, "<span class=\"diffrm\">", -1); } htmlize_to_blob(pOut, pLine->z, pLine->n); if( cPrefix!=' ' ){ blob_append(pOut, "</span>", -1); } }else{ blob_append(pOut, pLine->z, pLine->n); } blob_append(pOut, "\n", 1); } /* ** Add two line numbers to the beginning of an output line for a context ** diff. One or the other of the two numbers might be zero, which means |
︙ | ︙ | |||
400 401 402 403 404 405 406 | appendDiffLine(pOut, '+', &B[b+j], html, pRe); } b += m; if( i<nr-1 ){ m = R[r+i*3+3]; for(j=0; j<m; j++){ if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html); | | | | 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | appendDiffLine(pOut, '+', &B[b+j], html, pRe); } b += m; if( i<nr-1 ){ m = R[r+i*3+3]; for(j=0; j<m; j++){ if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html); appendDiffLine(pOut, ' ', &A[a+j], html, 0); } b += m; a += m; } } /* Show the final common area */ assert( nr==i ); m = R[r+nr*3]; if( m>nContext ) m = nContext; for(j=0; j<m; j++){ if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html); appendDiffLine(pOut, ' ', &A[a+j], html, 0); } } } /* ** Status of a single output line */ |
︙ | ︙ | |||
474 475 476 477 478 479 480 | ** rendering to width bytes if col is SBS_TXTA and escHtml is false. ** ** This comment contains multibyte unicode characters (ü, Æ, ð) in order ** to test the ability of the diff code to handle such characters. */ static void sbsWriteText(SbsLine *p, DLine *pLine, int col){ Blob *pCol = p->apCols[col]; | | | 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | ** rendering to width bytes if col is SBS_TXTA and escHtml is false. ** ** This comment contains multibyte unicode characters (ü, Æ, ð) in order ** to test the ability of the diff code to handle such characters. */ static void sbsWriteText(SbsLine *p, DLine *pLine, int col){ Blob *pCol = p->apCols[col]; int n = pLine->n; int i; /* Number of input characters consumed */ int k; /* Cursor position */ int needEndSpan = 0; const char *zIn = pLine->z; int w = p->width; int colorize = p->escHtml; if( colorize && p->pRe && re_dline_match(p->pRe, pLine, 1)==0 ){ |
︙ | ︙ | |||
546 547 548 549 550 551 552 | static void sbsWriteColumn(Blob *pOut, Blob *pCol, int col){ blob_appendf(pOut, "<td><div class=\"diff%scol\">\n" "<pre>\n" "%s" "</pre>\n" "</div></td>\n", | | | 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | static void sbsWriteColumn(Blob *pOut, Blob *pCol, int col){ blob_appendf(pOut, "<td><div class=\"diff%scol\">\n" "<pre>\n" "%s" "</pre>\n" "</div></td>\n", (col % 3) ? (col == SBS_MKR ? "mkr" : "txt") : "ln", blob_str(pCol) ); } /* ** Append a separator line to column iCol */ |
︙ | ︙ | |||
718 719 720 721 722 723 724 | int nLeftDiff; /* nLeft - nPrefix - nSuffix */ int nRightDiff; /* nRight - nPrefix - nSuffix */ int aLCS[4]; /* Bounds of common middle segment */ static const char zClassRm[] = "<span class=\"diffrm\">"; static const char zClassAdd[] = "<span class=\"diffadd\">"; static const char zClassChng[] = "<span class=\"diffchng\">"; | | | | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | int nLeftDiff; /* nLeft - nPrefix - nSuffix */ int nRightDiff; /* nRight - nPrefix - nSuffix */ int aLCS[4]; /* Bounds of common middle segment */ static const char zClassRm[] = "<span class=\"diffrm\">"; static const char zClassAdd[] = "<span class=\"diffadd\">"; static const char zClassChng[] = "<span class=\"diffchng\">"; nLeft = pLeft->n; zLeft = pLeft->z; nRight = pRight->n; zRight = pRight->z; nShort = nLeft<nRight ? nLeft : nRight; nPrefix = 0; while( nPrefix<nShort && zLeft[nPrefix]==zRight[nPrefix] ){ nPrefix++; } |
︙ | ︙ | |||
801 802 803 804 805 806 807 | p->zStart = zClassRm; }else{ p->zStart = zClassChng; } p->iStart2 = nPrefix + aLCS[1]; p->iEnd2 = nLeft - nSuffix; p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng; | | | | 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 | p->zStart = zClassRm; }else{ p->zStart = zClassChng; } p->iStart2 = nPrefix + aLCS[1]; p->iEnd2 = nLeft - nSuffix; p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng; sbsSimplifyLine(p, zLeft); sbsWriteText(p, pLeft, SBS_TXTA); sbsWriteMarker(p, " | ", "|"); sbsWriteLineno(p, lnRight, SBS_LNB); p->iStart = nPrefix; p->iEnd = nPrefix + aLCS[2]; if( aLCS[0]==0 ){ sbsShiftLeft(p, pRight->z); p->zStart = zClassAdd; }else{ p->zStart = zClassChng; } p->iStart2 = nPrefix + aLCS[3]; p->iEnd2 = nRight - nSuffix; p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng; sbsSimplifyLine(p, zRight); sbsWriteText(p, pRight, SBS_TXTB); return; } /* If all else fails, show a single big change between left and right */ sbsWriteLineno(p, lnLeft, SBS_LNA); p->iStart2 = p->iEnd2 = 0; |
︙ | ︙ | |||
866 867 868 869 870 871 872 | int score; /* Final score. 0..100 */ unsigned char c; /* Character being examined */ unsigned char aFirst[256]; /* aFirst[X] = index in zB[] of first char X */ unsigned char aNext[252]; /* aNext[i] = index in zB[] of next zB[i] char */ zA = pA->z; zB = pB->z; | | | | 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 | int score; /* Final score. 0..100 */ unsigned char c; /* Character being examined */ unsigned char aFirst[256]; /* aFirst[X] = index in zB[] of first char X */ unsigned char aNext[252]; /* aNext[i] = index in zB[] of next zB[i] char */ zA = pA->z; zB = pB->z; nA = pA->n; nB = pB->n; while( nA>0 && fossil_isspace(zA[0]) ){ nA--; zA++; } while( nA>0 && fossil_isspace(zA[nA-1]) ){ nA--; } while( nB>0 && fossil_isspace(zB[0]) ){ nB--; zB++; } while( nB>0 && fossil_isspace(zB[nB-1]) ){ nB--; } if( nA>250 ) nA = 250; if( nB>250 ) nB = 250; avg = (nA+nB)/2; |
︙ | ︙ | |||
1303 1304 1305 1306 1307 1308 1309 | s.iStart = s.iEnd = -1; sbsWriteText(&s, &A[a+j], SBS_TXTA); sbsWriteMarker(&s, " ", ""); sbsWriteLineno(&s, b+j, SBS_LNB); sbsWriteText(&s, &B[b+j], SBS_TXTB); } } | | | 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 | s.iStart = s.iEnd = -1; sbsWriteText(&s, &A[a+j], SBS_TXTA); sbsWriteMarker(&s, " ", ""); sbsWriteLineno(&s, b+j, SBS_LNB); sbsWriteText(&s, &B[b+j], SBS_TXTB); } } if( s.escHtml && blob_size(s.apCols[SBS_LNA])>0 ){ blob_append(pOut, "<table class=\"sbsdiffcols\"><tr>\n", -1); for(i=SBS_LNA; i<=SBS_TXTB; i++){ sbsWriteColumn(pOut, s.apCols[i], i); blob_reset(s.apCols[i]); } blob_append(pOut, "</tr></table>\n", -1); |
︙ | ︙ | |||
1335 1336 1337 1338 1339 1340 1341 | int i, j; /* Loop counters */ int k; /* Length of a candidate subsequence */ int iSXb = iS1; /* Best match so far */ int iSYb = iS2; /* Best match so far */ for(i=iS1; i<iE1-mxLength; i++){ for(j=iS2; j<iE2-mxLength; j++){ | | | | | 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 | int i, j; /* Loop counters */ int k; /* Length of a candidate subsequence */ int iSXb = iS1; /* Best match so far */ int iSYb = iS2; /* Best match so far */ for(i=iS1; i<iE1-mxLength; i++){ for(j=iS2; j<iE2-mxLength; j++){ if( !p->same_fn(&p->aFrom[i], &p->aTo[j]) ) continue; if( mxLength && !p->same_fn(&p->aFrom[i+mxLength], &p->aTo[j+mxLength]) ){ continue; } k = 1; while( i+k<iE1 && j+k<iE2 && p->same_fn(&p->aFrom[i+k],&p->aTo[j+k]) ){ k++; } if( k>mxLength ){ iSXb = i; iSYb = j; mxLength = k; } |
︙ | ︙ | |||
1406 1407 1408 1409 1410 1411 1412 | iSYb = iSYp = iS2; iEYb = iEYp = iS2; mid = (iE1 + iS1)/2; for(i=iS1; i<iE1; i++){ int limit = 0; j = p->aTo[p->aFrom[i].h % p->nTo].iHash; while( j>0 | | | | | 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 | iSYb = iSYp = iS2; iEYb = iEYp = iS2; mid = (iE1 + iS1)/2; for(i=iS1; i<iE1; i++){ int limit = 0; j = p->aTo[p->aFrom[i].h % p->nTo].iHash; while( j>0 && (j-1<iS2 || j>=iE2 || !p->same_fn(&p->aFrom[i], &p->aTo[j-1])) ){ if( limit++ > 10 ){ j = 0; break; } j = p->aTo[j-1].iNext; } if( j==0 ) continue; assert( i>=iSXb && i>=iSXp ); if( i<iEXb && j>=iSYb && j<iEYb ) continue; if( i<iEXp && j>=iSYp && j<iEYp ) continue; iSX = i; iSY = j-1; pA = &p->aFrom[iSX-1]; pB = &p->aTo[iSY-1]; n = minInt(iSX-iS1, iSY-iS2); for(k=0; k<n && p->same_fn(pA,pB); k++, pA--, pB--){} iSX -= k; iSY -= k; iEX = i+1; iEY = j; pA = &p->aFrom[iEX]; pB = &p->aTo[iEY]; n = minInt(iE1-iEX, iE2-iEY); for(k=0; k<n && p->same_fn(pA,pB); k++, pA++, pB++){} iEX += k; iEY += k; skew = (iSX-iS1) - (iSY-iS2); if( skew<0 ) skew = -skew; dist = (iSX+iEX)/2 - mid; if( dist<0 ) dist = -dist; score = (iEX - iSX)*(sqlite3_int64)span - (skew + dist); |
︙ | ︙ | |||
1572 1573 1574 1575 1576 1577 1578 | */ static void diff_all(DContext *p){ int mnE, iS, iE1, iE2; /* Carve off the common header and footer */ iE1 = p->nFrom; iE2 = p->nTo; | | | | 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 | */ static void diff_all(DContext *p){ int mnE, iS, iE1, iE2; /* Carve off the common header and footer */ iE1 = p->nFrom; iE2 = p->nTo; while( iE1>0 && iE2>0 && p->same_fn(&p->aFrom[iE1-1], &p->aTo[iE2-1]) ){ iE1--; iE2--; } mnE = iE1<iE2 ? iE1 : iE2; for(iS=0; iS<mnE && p->same_fn(&p->aFrom[iS],&p->aTo[iS]); iS++){} /* do the difference */ if( iS>0 ){ appendTriple(p, iS, 0, 0); } diff_step(p, iS, iE1, iS, iE2); if( iE1<p->nFrom ){ |
︙ | ︙ | |||
1646 1647 1648 1649 1650 1651 1652 | lnFrom += cpy; lnTo += cpy; /* Shift insertions toward the beginning of the file */ while( cpy>0 && del==0 && ins>0 ){ DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of insert */ DLine *pBtm = &p->aTo[lnTo+ins-1]; /* Last line inserted */ | | | | | | 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 | lnFrom += cpy; lnTo += cpy; /* Shift insertions toward the beginning of the file */ while( cpy>0 && del==0 && ins>0 ){ DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of insert */ DLine *pBtm = &p->aTo[lnTo+ins-1]; /* Last line inserted */ if( p->same_fn(pTop, pBtm)==0 ) break; if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break; lnFrom--; lnTo--; p->aEdit[r]--; p->aEdit[r+3]++; cpy--; } /* Shift insertions toward the end of the file */ while( r+3<p->nEdit && p->aEdit[r+3]>0 && del==0 && ins>0 ){ DLine *pTop = &p->aTo[lnTo]; /* First line inserted */ DLine *pBtm = &p->aTo[lnTo+ins]; /* First line past end of insert */ if( p->same_fn(pTop, pBtm)==0 ) break; if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop+1)+LENGTH(pBtm) ) break; lnFrom++; lnTo++; p->aEdit[r]++; p->aEdit[r+3]--; cpy++; } /* Shift deletions toward the beginning of the file */ while( cpy>0 && del>0 && ins==0 ){ DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of delete */ DLine *pBtm = &p->aFrom[lnFrom+del-1]; /* Last line deleted */ if( p->same_fn(pTop, pBtm)==0 ) break; if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break; lnFrom--; lnTo--; p->aEdit[r]--; p->aEdit[r+3]++; cpy--; } /* Shift deletions toward the end of the file */ while( r+3<p->nEdit && p->aEdit[r+3]>0 && del>0 && ins==0 ){ DLine *pTop = &p->aFrom[lnFrom]; /* First line deleted */ DLine *pBtm = &p->aFrom[lnFrom+del]; /* First line past end of delete */ if( p->same_fn(pTop, pBtm)==0 ) break; if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break; lnFrom++; lnTo++; p->aEdit[r]++; p->aEdit[r+3]--; cpy++; } |
︙ | ︙ | |||
1751 1752 1753 1754 1755 1756 1757 | int *text_diff( Blob *pA_Blob, /* FROM file */ Blob *pB_Blob, /* TO file */ Blob *pOut, /* Write diff here if not NULL */ ReCompiled *pRe, /* Only output changes where this Regexp matches */ u64 diffFlags /* DIFF_* flags defined above */ ){ | | | > > > > > > > | | > > > > > > > | | 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 | int *text_diff( Blob *pA_Blob, /* FROM file */ Blob *pB_Blob, /* TO file */ Blob *pOut, /* Write diff here if not NULL */ ReCompiled *pRe, /* Only output changes where this Regexp matches */ u64 diffFlags /* DIFF_* flags defined above */ ){ int ignoreWs; /* Ignore whitespace */ DContext c; if( diffFlags & DIFF_INVERT ){ Blob *pTemp = pA_Blob; pA_Blob = pB_Blob; pB_Blob = pTemp; } ignoreWs = (diffFlags & DIFF_IGNORE_ALLWS)!=0; blob_to_utf8_no_bom(pA_Blob, 0); blob_to_utf8_no_bom(pB_Blob, 0); /* Prepare the input files */ memset(&c, 0, sizeof(c)); if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){ c.same_fn = same_dline_ignore_allws; }else{ c.same_fn = same_dline; } c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), &c.nFrom, diffFlags); c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), &c.nTo, diffFlags); if( c.aFrom==0 || c.aTo==0 ){ fossil_free(c.aFrom); fossil_free(c.aTo); if( pOut ){ diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, diffFlags); } return 0; } /* Compute the difference */ diff_all(&c); if( ignoreWs && c.nEdit==6 && c.aEdit[1]==0 && c.aEdit[2]==0 ){ fossil_free(c.aFrom); fossil_free(c.aTo); fossil_free(c.aEdit); if( pOut ) diff_errmsg(pOut, DIFF_WHITESPACE_ONLY, diffFlags); return 0; } if( (diffFlags & DIFF_NOTTOOBIG)!=0 ){ int i, m, n; int *a = c.aEdit; int mx = c.nEdit; for(i=m=n=0; i<mx; i+=3){ m += a[i]; n += a[i+1]+a[i+2]; } if( n>10000 ){ fossil_free(c.aFrom); fossil_free(c.aTo); fossil_free(c.aEdit); if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, diffFlags); return 0; } } if( (diffFlags & DIFF_NOOPT)==0 ){ diff_optimize(&c); } |
︙ | ︙ | |||
1820 1821 1822 1823 1824 1825 1826 | } } /* ** Process diff-related command-line options and return an appropriate ** "diffFlags" integer. ** | | | | | | | | | > | > > > > > > > > > > > | 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 | } } /* ** Process diff-related command-line options and return an appropriate ** "diffFlags" integer. ** ** --brief Show filenames only DIFF_BRIEF ** -c|--context N N lines of context. DIFF_CONTEXT_MASK ** --html Format for HTML DIFF_HTML ** --invert Invert the diff DIFF_INVERT ** -n|--linenum Show line numbers DIFF_LINENO ** --noopt Disable optimization DIFF_NOOPT ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR ** --unified Unified diff. ~DIFF_SIDEBYSIDE ** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS ** -W|--width N N character lines. DIFF_WIDTH_MASK ** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE ** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS */ u64 diff_options(void){ u64 diffFlags = 0; const char *z; int f; if( find_option("ignore-trailing-space","Z",0)!=0 ){ diffFlags = DIFF_IGNORE_EOLWS; } if( find_option("ignore-all-space","w",0)!=0 ){ diffFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */ } if( find_option("strip-trailing-cr",0,0)!=0 ){ diffFlags |= DIFF_STRIP_EOLCR; } if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE; if( find_option("unified",0,0)!=0 ) diffFlags &= ~DIFF_SIDEBYSIDE; if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>=0 ){ if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK; diffFlags |= f + DIFF_CONTEXT_EX; } if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){ |
︙ | ︙ | |||
1926 1927 1928 1929 1930 1931 1932 | ** of the following structure. */ typedef struct Annotator Annotator; struct Annotator { DContext c; /* The diff-engine context */ struct AnnLine { /* Lines of the original files... */ const char *z; /* The text of the line */ | | > | > > > > > | > | | | | 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 | ** of the following structure. */ typedef struct Annotator Annotator; struct Annotator { DContext c; /* The diff-engine context */ struct AnnLine { /* Lines of the original files... */ const char *z; /* The text of the line */ short int n; /* Number of bytes (omitting trailing \n) */ short int iVers; /* Level at which tag was set */ } *aOrig; int nOrig; /* Number of elements in aOrig[] */ int nVers; /* Number of versions analyzed */ int bLimit; /* True if the iLimit was reached */ struct AnnVers { const char *zFUuid; /* File being analyzed */ const char *zMUuid; /* Check-in containing the file */ const char *zDate; /* Date of the check-in */ const char *zBgColor; /* Suggested background color */ const char *zUser; /* Name of user who did the check-in */ unsigned cnt; /* Number of lines contributed by this check-in */ } *aVers; /* For each check-in analyzed */ char **azVers; /* Names of versions analyzed */ }; /* ** Initialize the annotation process by specifying the file that is ** to be annotated. The annotator takes control of the input Blob and ** will release it when it is finished with it. */ static int annotation_start(Annotator *p, Blob *pInput, u64 diffFlags){ int i; memset(p, 0, sizeof(*p)); if( (diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){ p->c.same_fn = same_dline_ignore_allws; }else{ p->c.same_fn = same_dline; } p->c.aTo = break_into_lines(blob_str(pInput), blob_size(pInput),&p->c.nTo, diffFlags); if( p->c.aTo==0 ){ return 1; } p->aOrig = fossil_malloc( sizeof(p->aOrig[0])*p->c.nTo ); for(i=0; i<p->c.nTo; i++){ p->aOrig[i].z = p->c.aTo[i].z; p->aOrig[i].n = p->c.aTo[i].n; p->aOrig[i].iVers = -1; } p->nOrig = p->c.nTo; return 0; } /* ** The input pParent is the next most recent ancestor of the file ** being annotated. Do another step of the annotation. Return true ** if additional annotation is required. zPName is the tag to insert ** on each line of the file being annotated that was contributed by ** pParent. Memory to hold zPName is leaked. */ static int annotation_step(Annotator *p, Blob *pParent, int iVers, u64 diffFlags){ int i, j; int lnTo; /* Prepare the parent file to be diffed */ p->c.aFrom = break_into_lines(blob_str(pParent), blob_size(pParent), &p->c.nFrom, diffFlags); if( p->c.aFrom==0 ){ return 1; } /* Compute the differences going from pParent to the file being ** annotated. */ diff_all(&p->c); |
︙ | ︙ | |||
2015 2016 2017 2018 2019 2020 2021 | free(p->c.aFrom); /* Return no errors */ return 0; } | | | | | > | > | > > | | 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 | free(p->c.aFrom); /* Return no errors */ return 0; } /* Annotation flags (any DIFF flag can be used as Annotation flag as well) */ #define ANN_FILE_VERS (((u64)0x20)<<32) /* Show file vers rather than commit vers */ #define ANN_FILE_ANCEST (((u64)0x40)<<32) /* Prefer check-ins in the ANCESTOR table */ /* ** Compute a complete annotation on a file. The file is identified ** by its filename number (filename.fnid) and the baseline in which ** it was checked in (mlink.mid). */ static void annotate_file( Annotator *p, /* The annotator */ int fnid, /* The name of the file to be annotated */ int mid, /* Use the version of the file in this check-in */ int iLimit, /* Limit the number of levels if greater than zero */ u64 annFlags /* Flags to alter the annotation */ ){ Blob toAnnotate; /* Text of the final (mid) version of the file */ Blob step; /* Text of previous revision */ int rid; /* Artifact ID of the file being annotated */ Stmt q; /* Query returning all ancestor versions */ Stmt ins; /* Inserts into the temporary VSEEN table */ int cnt = 0; /* Number of versions examined */ /* Initialize the annotation */ rid = db_int(0, "SELECT fid FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid); if( rid==0 ){ fossil_fatal("file #%d is unchanged in manifest #%d", fnid, mid); } if( !content_get(rid, &toAnnotate) ){ fossil_fatal("unable to retrieve content of artifact #%d", rid); } if( iLimit<=0 ) iLimit = 1000000000; blob_to_utf8_no_bom(&toAnnotate, 0); annotation_start(p, &toAnnotate, annFlags); db_begin_transaction(); db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS vseen(rid INTEGER PRIMARY KEY);" "DELETE FROM vseen;" ); db_prepare(&ins, "INSERT OR IGNORE INTO vseen(rid) VALUES(:rid)"); db_prepare(&q, "SELECT (SELECT uuid FROM blob WHERE rid=mlink.fid)," " (SELECT uuid FROM blob WHERE rid=mlink.mid)," " date(event.mtime)," " coalesce(event.euser,event.user)," " mlink.pid" " FROM mlink, event" " WHERE mlink.fid=:rid" " AND event.objid=mlink.mid" " AND mlink.pid NOT IN vseen" " ORDER BY %s event.mtime", (annFlags & ANN_FILE_ANCEST)!=0 ? "(mlink.mid IN (SELECT rid FROM ancestor)) DESC,":"" ); db_bind_int(&q, ":rid", rid); if( iLimit==0 ) iLimit = 1000000000; while( rid && iLimit>cnt && db_step(&q)==SQLITE_ROW ){ int prevId = db_column_int(&q, 4); p->aVers = fossil_realloc(p->aVers, (p->nVers+1)*sizeof(p->aVers[0])); p->aVers[p->nVers].zFUuid = fossil_strdup(db_column_text(&q, 0)); p->aVers[p->nVers].zMUuid = fossil_strdup(db_column_text(&q, 1)); p->aVers[p->nVers].zDate = fossil_strdup(db_column_text(&q, 2)); p->aVers[p->nVers].zUser = fossil_strdup(db_column_text(&q, 3)); if( p->nVers ){ content_get(rid, &step); blob_to_utf8_no_bom(&step, 0); annotation_step(p, &step, p->nVers-1, annFlags); blob_reset(&step); } p->nVers++; db_bind_int(&ins, ":rid", rid); db_step(&ins); db_reset(&ins); db_reset(&q); |
︙ | ︙ | |||
2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 | /* ** Return a color from a gradient. */ unsigned gradient_color(unsigned c1, unsigned c2, int n, int i){ unsigned c; /* Result color */ unsigned x1, x2; if( i==0 || n==0 ) return c1; x1 = (c1>>16)&0xff; x2 = (c2>>16)&0xff; c = (x1*(n-i) + x2*i)/n<<16 & 0xff0000; x1 = (c1>>8)&0xff; x2 = (c2>>8)&0xff; c |= (x1*(n-i) + x2*i)/n<<8 & 0xff00; x1 = c1&0xff; x2 = c2&0xff; c |= (x1*(n-i) + x2*i)/n & 0xff; return c; } /* ** WEBPAGE: annotate ** ** Query parameters: ** ** checkin=ID The manifest ID at which to start the annotation ** filename=FILENAME The filename. ** filevers Show file versions rather than check-in versions ** log=BOOLEAN Show a log of versions analyzed ** limit=N Limit the search depth to N ancestors */ void annotation_page(void){ int mid; int fnid; int i; int iLimit; /* Depth limit */ | > > > | > > | > > > > > > | > > > > > > > > > | | | | | | | | | | | | | | | > > | | | | | > > > > > > > > > > > > | | | | | > > > | | > > | | | > > | > > > > > > > > > > > > | 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 | /* ** Return a color from a gradient. */ unsigned gradient_color(unsigned c1, unsigned c2, int n, int i){ unsigned c; /* Result color */ unsigned x1, x2; if( i==0 || n==0 ) return c1; else if(i>=n) return c2; x1 = (c1>>16)&0xff; x2 = (c2>>16)&0xff; c = (x1*(n-i) + x2*i)/n<<16 & 0xff0000; x1 = (c1>>8)&0xff; x2 = (c2>>8)&0xff; c |= (x1*(n-i) + x2*i)/n<<8 & 0xff00; x1 = c1&0xff; x2 = c2&0xff; c |= (x1*(n-i) + x2*i)/n & 0xff; return c; } /* ** WEBPAGE: annotate ** WEBPAGE: blame ** WEBPAGE: praise ** ** Query parameters: ** ** checkin=ID The manifest ID at which to start the annotation ** filename=FILENAME The filename. ** filevers Show file versions rather than check-in versions ** log=BOOLEAN Show a log of versions analyzed ** limit=N Limit the search depth to N ancestors */ void annotation_page(void){ int mid; int fnid; int i; int iLimit; /* Depth limit */ u64 annFlags = (ANN_FILE_ANCEST|DIFF_STRIP_EOLCR); int showLog = 0; /* True to display the log */ int ignoreWs = 0; /* Ignore whitespace */ const char *zFilename; /* Name of file to annotate */ const char *zCI; /* The check-in containing zFilename */ Annotator ann; HQuery url; struct AnnVers *p; unsigned clr1, clr2, clr; int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */ /* Gather query parameters */ showLog = atoi(PD("log","1")); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( exclude_spiders("annotate") ) return; load_control(); mid = name_to_typed_rid(PD("checkin","0"),"ci"); zFilename = P("filename"); fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); if( mid==0 || fnid==0 ){ fossil_redirect_home(); } iLimit = atoi(PD("limit","20")); if( P("filevers") ) annFlags |= ANN_FILE_VERS; ignoreWs = P("w")!=0; if( ignoreWs ) annFlags |= DIFF_IGNORE_ALLWS; if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d AND fnid=%d",mid,fnid) ){ fossil_redirect_home(); } /* compute the annotation */ compute_direct_ancestors(mid, 10000000); annotate_file(&ann, fnid, mid, iLimit, annFlags); zCI = ann.aVers[0].zMUuid; /* generate the web page */ style_header("Annotation For %h", zFilename); if( bBlame ){ url_initialize(&url, "blame"); }else{ url_initialize(&url, "annotate"); } url_add_parameter(&url, "checkin", P("checkin")); url_add_parameter(&url, "filename", zFilename); if( iLimit!=20 ){ url_add_parameter(&url, "limit", sqlite3_mprintf("%d", iLimit)); } url_add_parameter(&url, "log", showLog ? "1" : "0"); if( ignoreWs ){ url_add_parameter(&url, "w", ""); style_submenu_element("Show Whitespace Changes", "Show Whitespace Changes", "%s", url_render(&url, "w", 0, 0, 0)); }else{ style_submenu_element("Ignore Whitespace", "Ignore Whitespace", "%s", url_render(&url, "w", "", 0, 0)); } if( showLog ){ style_submenu_element("Hide Log", "Hide Log", "%s", url_render(&url, "log", "0", 0, 0)); }else{ style_submenu_element("Show Log", "Show Log", "%s", url_render(&url, "log", "1", 0, 0)); } if( ann.bLimit ){ char *z1, *z2; style_submenu_element("All Ancestors", "All Ancestors", "%s", url_render(&url, "limit", "-1", 0, 0)); z1 = sqlite3_mprintf("%d Ancestors", iLimit+20); z2 = sqlite3_mprintf("%d", iLimit+20); style_submenu_element(z1, z1, "%s", url_render(&url, "limit", z2, 0, 0)); } if( iLimit>20 ){ style_submenu_element("20 Ancestors", "20 Ancestors", "%s", url_render(&url, "limit", "20", 0, 0)); } if( skin_detail_boolean("white-foreground") ){ clr1 = 0xa04040; clr2 = 0x4059a0; }else{ clr1 = 0xffb5b5; /* Recent changes: red (hot) */ clr2 = 0xb5e0ff; /* Older changes: blue (cold) */ } for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){ clr = gradient_color(clr1, clr2, ann.nVers-1, i); ann.aVers[i].zBgColor = mprintf("#%06x", clr); } if( showLog ){ char *zLink = href("%R/finfo?name=%t&ci=%!S",zFilename,zCI); @ <h2>Ancestors of %z(zLink)%h(zFilename)</a> analyzed:</h2> @ <ol> for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){ @ <li><span style='background-color:%s(p->zBgColor);'>%s(p->zDate) @ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a> @ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a> @ </span> #if 0 if( i>0 ){ char *zLink = xhref("target='infowindow'", "%R/fdiff?v1=%S&v2=%S&sbs=1", p->zFUuid,ann.aVers[0].zFUuid); @ %z(zLink)[diff-to-top]</a> if( i>1 ){ zLink = xhref("target='infowindow'", "%R/fdiff?v1=%S&v2=%S&sbs=1", p->zFUuid,p[-1].zFUuid); @ %z(zLink)[diff-to-previous]</a> } } #endif } @ </ol> @ <hr> } if( !ann.bLimit ){ @ <h2>Origin for each line in @ %z(href("%R/finfo?name=%h&ci=%!S", zFilename, zCI))%h(zFilename)</a> @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2> iLimit = ann.nVers+10; }else{ @ <h2>Lines added by the %d(iLimit) most recent ancestors of @ %z(href("%R/finfo?name=%h&ci=%!S", zFilename, zCI))%h(zFilename)</a> @ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2> } @ <pre> for(i=0; i<ann.nOrig; i++){ int iVers = ann.aOrig[i].iVers; char *z = (char*)ann.aOrig[i].z; int n = ann.aOrig[i].n; char zPrefix[300]; z[n] = 0; if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1; if( bBlame ){ if( iVers>=0 ){ struct AnnVers *p = ann.aVers+iVers; char *zLink = xhref("target='infowindow'", "%R/info/%!S", p->zMUuid); sqlite3_snprintf(sizeof(zPrefix), zPrefix, "<span style='background-color:%s'>" "%s%.10s</a> %s</span> %13.13s:", p->zBgColor, zLink, p->zMUuid, p->zDate, p->zUser); fossil_free(zLink); }else{ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%36s", ""); } }else{ if( iVers>=0 ){ struct AnnVers *p = ann.aVers+iVers; char *zLink = xhref("target='infowindow'", "%R/info/%!S", p->zMUuid); sqlite3_snprintf(sizeof(zPrefix), zPrefix, "<span style='background-color:%s'>" "%s%.10s</a> %s</span> %4d:", p->zBgColor, zLink, p->zMUuid, p->zDate, i+1); fossil_free(zLink); }else{ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%22s%4d:", "", i+1); } } @ %s(zPrefix) %h(z) } @ </pre> style_footer(); } /* ** COMMAND: annotate ** COMMAND: blame ** COMMAND: praise ** ** %fossil (annotate|blame|praise) ?OPTIONS? FILENAME ** ** Output the text of a file with markings to show when each line of ** the file was last modified. The "annotate" command shows line numbers ** and omits the username. The "blame" and "praise" commands show the user ** who made each check-in and omits the line number. ** ** Options: ** --filevers Show file version numbers rather than check-in versions ** -l|--log List all versions analyzed ** -n|--limit N Only look backwards in time by N versions ** -w|--ignore-all-space Ignore white space when comparing lines ** -Z|--ignore-trailing-space Ignore whitespace at line end ** ** See also: info, finfo, timeline */ void annotate_cmd(void){ int fnid; /* Filename ID */ int fid; /* File instance ID */ int mid; /* Manifest where file was checked in */ int cid; /* Checkout ID */ Blob treename; /* FILENAME translated to canonical form */ char *zFilename; /* Canonical filename */ Annotator ann; /* The annotation of the file */ int i; /* Loop counter */ const char *zLimit; /* The value to the -n|--limit option */ int iLimit; /* How far back in time to look */ int showLog; /* True to show the log */ int fileVers; /* Show file version instead of check-in versions */ u64 annFlags = 0; /* Flags to control annotation properties */ int bBlame = 0; /* True for BLAME output. False for ANNOTATE. */ bBlame = g.argv[1][0]!='a'; zLimit = find_option("limit","n",1); if( zLimit==0 || zLimit[0]==0 ) zLimit = "-1"; iLimit = atoi(zLimit); showLog = find_option("log","l",0)!=0; if( find_option("ignore-trailing-space","Z",0)!=0 ){ annFlags = DIFF_IGNORE_EOLWS; } if( find_option("ignore-all-space","w",0)!=0 ){ annFlags = DIFF_IGNORE_ALLWS; /* stronger than DIFF_IGNORE_EOLWS */ } fileVers = find_option("filevers",0,0)!=0; db_must_be_within_tree(); /* We should be done with options.. */ verify_all_options(); if( g.argc<3 ) { usage("FILENAME"); } file_tree_name(g.argv[2], &treename, 1); zFilename = blob_str(&treename); fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); if( fnid==0 ){ |
︙ | ︙ | |||
2326 2327 2328 2329 2330 2331 2332 | mid = db_int(0, "SELECT mlink.mid FROM mlink, ancestor " " WHERE mlink.fid=%d AND mlink.fnid=%d AND mlink.mid=ancestor.rid" " ORDER BY ancestor.generation ASC LIMIT 1", fid, fnid); if( mid==0 ){ fossil_fatal("unable to find manifest"); } | | | | < > > | < | | | | | > > > > > | > | | > > | 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 | mid = db_int(0, "SELECT mlink.mid FROM mlink, ancestor " " WHERE mlink.fid=%d AND mlink.fnid=%d AND mlink.mid=ancestor.rid" " ORDER BY ancestor.generation ASC LIMIT 1", fid, fnid); if( mid==0 ){ fossil_fatal("unable to find manifest"); } annFlags |= (ANN_FILE_ANCEST|DIFF_STRIP_EOLCR); annotate_file(&ann, fnid, mid, iLimit, annFlags); if( showLog ){ struct AnnVers *p; for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){ fossil_print("version %3d: %s %S file %S\n", i+1, p->zDate, p->zMUuid, p->zFUuid); } fossil_print("---------------------------------------------------\n"); } for(i=0; i<ann.nOrig; i++){ int iVers = ann.aOrig[i].iVers; char *z = (char*)ann.aOrig[i].z; int n = ann.aOrig[i].n; struct AnnVers *p; if( iLimit>ann.nVers && iVers<0 ) iVers = ann.nVers-1; p = ann.aVers + iVers; if( bBlame ){ if( iVers>=0 ){ fossil_print("%S %s %13.13s: %.*s\n", fileVers ? p->zFUuid : p->zMUuid, p->zDate, p->zUser, n, z); }else{ fossil_print("%35s %.*s\n", "", n, z); } }else{ if( iVers>=0 ){ fossil_print("%S %s %5d: %.*s\n", fileVers ? p->zFUuid : p->zMUuid, p->zDate, i+1, n, z); }else{ fossil_print("%21s %5d: %.*s\n", "", i+1, n, z); } } } } |
Added src/diff.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | # The "diff --tk" command outputs prepends a "set fossilcmd {...}" line # to this file, then runs this file using "tclsh" in order to display the # graphical diff in a separate window. A typical "set fossilcmd" line # looks like this: # # set fossilcmd {| "./fossil" diff --html -y -i -v} # # This header comment is stripped off by the "mkbuiltin.c" program. # set prog { package require Tk array set CFG { TITLE {Fossil Diff} LN_COL_BG #dddddd LN_COL_FG #444444 TXT_COL_BG #ffffff TXT_COL_FG #000000 MKR_COL_BG #444444 MKR_COL_FG #dddddd CHNG_BG #d0d0ff ADD_BG #c0ffc0 RM_BG #ffc0c0 HR_FG #888888 HR_PAD_TOP 4 HR_PAD_BTM 8 FN_BG #444444 FN_FG #ffffff FN_PAD 5 ERR_FG #ee0000 PADX 5 WIDTH 80 HEIGHT 45 LB_HEIGHT 25 } if {![namespace exists ttk]} { interp alias {} ::ttk::scrollbar {} ::scrollbar interp alias {} ::ttk::menubutton {} ::menubutton } proc dehtml {x} { set x [regsub -all {<[^>]*>} $x {}] return [string map {& & < < > > ' ' " \"} $x] } proc cols {} { return [list .lnA .txtA .mkr .lnB .txtB] } proc colType {c} { regexp {[a-z]+} $c type return $type } proc getLine {difftxt N iivar} { upvar $iivar ii if {$ii>=$N} {return -1} set x [lindex $difftxt $ii] incr ii return $x } proc readDiffs {fossilcmd} { global difftxt if {![info exists difftxt]} { set in [open $fossilcmd r] fconfigure $in -encoding utf-8 set difftxt [split [read $in] \n] close $in } set N [llength $difftxt] set ii 0 set nDiffs 0 array set widths {txt 0 ln 0 mkr 0} while {[set line [getLine $difftxt $N ii]] != -1} { set fn2 {} if {![regexp {^=+ (.*?) =+ versus =+ (.*?) =+$} $line all fn fn2] && ![regexp {^=+ (.*?) =+$} $line all fn] } { continue } set errMsg "" set line [getLine $difftxt $N ii] if {[string compare -length 6 $line "<table"] && ![regexp {<p[^>]*>(.+)} $line - errMsg]} { continue } incr nDiffs set idx [expr {$nDiffs > 1 ? [.txtA index end] : "1.0"}] .wfiles.lb insert end $fn foreach c [cols] { if {$nDiffs > 1} { $c insert end \n - } if {[colType $c] eq "txt"} { $c insert end $fn\n fn if {$fn2!=""} {set fn $fn2} } else { $c insert end \n fn } $c insert end \n - if {$errMsg ne ""} continue while {[getLine $difftxt $N ii] ne "<pre>"} continue set type [colType $c] set str {} while {[set line [getLine $difftxt $N ii]] ne "</pre>"} { set len [string length [dehtml $line]] if {$len > $widths($type)} { set widths($type) $len } append str $line\n } set re {<span class="diff([a-z]+)">([^<]*)</span>} # Use \r as separator since it can't appear in the diff output (it gets # converted to a space). set str [regsub -all $re $str "\r\\1\r\\2\r"] foreach {pre class mid} [split $str \r] { if {$class ne ""} { $c insert end [dehtml $pre] - [dehtml $mid] [list $class -] } else { $c insert end [dehtml $pre] - } } } if {$errMsg ne ""} { foreach c {.txtA .txtB} {$c insert end [string trim $errMsg] err} foreach c [cols] {$c insert end \n -} } } foreach c [cols] { set type [colType $c] if {$type ne "txt"} { $c config -width $widths($type) } $c config -state disabled } if {$nDiffs <= [.wfiles.lb cget -height]} { .wfiles.lb config -height $nDiffs grid remove .wfiles.sb } return $nDiffs } proc viewDiff {idx} { .txtA yview $idx .txtA xview moveto 0 } proc cycleDiffs {{reverse 0}} { if {$reverse} { set range [.txtA tag prevrange fn @0,0 1.0] if {$range eq ""} { viewDiff {fn.last -1c} } else { viewDiff [lindex $range 0] } } else { set range [.txtA tag nextrange fn {@0,0 +1c} end] if {$range eq "" || [lindex [.txtA yview] 1] == 1} { viewDiff fn.first } else { viewDiff [lindex $range 0] } } } proc xvis {col} { set view [$col xview] return [expr {[lindex $view 1]-[lindex $view 0]}] } proc scroll-x {args} { set c .txt[expr {[xvis .txtA] < [xvis .txtB] ? "A" : "B"}] eval $c xview $args } interp alias {} scroll-y {} .txtA yview proc noop {args} {} proc enableSync {axis} { update idletasks interp alias {} sync-$axis {} rename _sync-$axis sync-$axis } proc disableSync {axis} { rename sync-$axis _sync-$axis interp alias {} sync-$axis {} noop } proc sync-x {col first last} { disableSync x $col xview moveto [expr {$first*[xvis $col]/($last-$first)}] foreach side {A B} { set sb .sbx$side set xview [.txt$side xview] if {[lindex $xview 0] > 0 || [lindex $xview 1] < 1} { grid $sb eval $sb set $xview } else { grid remove $sb } } enableSync x } proc sync-y {first last} { disableSync y foreach c [cols] { $c yview moveto $first } if {$first > 0 || $last < 1} { grid .sby .sby set $first $last } else { grid remove .sby } enableSync y } wm withdraw . wm title . $CFG(TITLE) wm iconname . $CFG(TITLE) bind . <q> exit bind . <Destroy> {after 0 exit} bind . <Tab> {cycleDiffs; break} bind . <<PrevWindow>> {cycleDiffs 1; break} bind . <Return> { event generate .bb.files <1> event generate .bb.files <ButtonRelease-1> break } foreach {key axis args} { Up y {scroll -5 units} k y {scroll -5 units} Down y {scroll 5 units} j y {scroll 5 units} Left x {scroll -5 units} h x {scroll -5 units} Right x {scroll 5 units} l x {scroll 5 units} Prior y {scroll -1 page} b y {scroll -1 page} Next y {scroll 1 page} space y {scroll 1 page} Home y {moveto 0} g y {moveto 0} End y {moveto 1} } { bind . <$key> "scroll-$axis $args; break" bind . <Shift-$key> continue } frame .bb ::ttk::menubutton .bb.files -text "Files" toplevel .wfiles wm withdraw .wfiles update idletasks wm transient .wfiles . wm overrideredirect .wfiles 1 listbox .wfiles.lb -width 0 -height $CFG(LB_HEIGHT) -activestyle none \ -yscroll {.wfiles.sb set} ::ttk::scrollbar .wfiles.sb -command {.wfiles.lb yview} grid .wfiles.lb .wfiles.sb -sticky ns bind .bb.files <1> { set x [winfo rootx %W] set y [expr {[winfo rooty %W]+[winfo height %W]}] wm geometry .wfiles +$x+$y wm deiconify .wfiles focus .wfiles.lb } bind .wfiles <FocusOut> {wm withdraw .wfiles} bind .wfiles <Escape> {focus .} foreach evt {1 Return} { bind .wfiles.lb <$evt> { catch { set idx [lindex [.txtA tag ranges fn] [expr {[%W curselection]*2}]] viewDiff $idx } focus . break } } bind .wfiles.lb <Motion> { %W selection clear 0 end %W selection set @%x,%y } foreach {side syncCol} {A .txtB B .txtA} { set ln .ln$side text $ln $ln tag config - -justify right set txt .txt$side text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \ -xscroll "sync-x $syncCol" catch {$txt config -tabstyle wordprocessor} ;# Required for Tk>=8.5 foreach tag {add rm chng} { $txt tag config $tag -background $CFG([string toupper $tag]_BG) $txt tag lower $tag } $txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \ -justify center $txt tag config err -foreground $CFG(ERR_FG) } text .mkr foreach c [cols] { set keyPrefix [string toupper [colType $c]]_COL_ if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}} $c config -bg $CFG(${keyPrefix}BG) -fg $CFG(${keyPrefix}FG) -borderwidth 0 \ -padx $CFG(PADX) -yscroll sync-y $c tag config hr -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \ -foreground $CFG(HR_FG) $c tag config fn -spacing1 $CFG(FN_PAD) -spacing3 $CFG(FN_PAD) bindtags $c ". $c Text all" bind $c <1> {focus %W} } ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal frame .spacer if {[readDiffs $fossilcmd] == 0} { tk_messageBox -type ok -title $CFG(TITLE) -message "No changes" exit } update idletasks proc saveDiff {} { set fn [tk_getSaveFile] if {$fn==""} return set out [open $fn wb] puts $out "#!/usr/bin/tclsh\n#\n# Run this script using 'tclsh' or 'wish'" puts $out "# to see the graphical diff.\n#" puts $out "set fossilcmd {}" puts $out "set prog [list $::prog]" puts $out "set difftxt \173" foreach e $::difftxt {puts $out [list $e]} puts $out "\175" puts $out "eval \$prog" close $out } proc invertDiff {} { global CFG array set x [grid info .txtA] if {$x(-column)==1} { grid config .lnB -column 0 grid config .txtB -column 1 .txtB tag config add -background $CFG(RM_BG) grid config .lnA -column 3 grid config .txtA -column 4 .txtA tag config rm -background $CFG(ADD_BG) } else { grid config .lnA -column 0 grid config .txtA -column 1 .txtA tag config rm -background $CFG(RM_BG) grid config .lnB -column 3 grid config .txtB -column 4 .txtB tag config add -background $CFG(ADD_BG) } .mkr config -state normal set clt [.mkr search -all < 1.0 end] set cgt [.mkr search -all > 1.0 end] foreach c $clt {.mkr replace $c "$c +1 chars" >} foreach c $cgt {.mkr replace $c "$c +1 chars" <} .mkr config -state disabled } ::ttk::button .bb.quit -text {Quit} -command exit ::ttk::button .bb.invert -text {Invert} -command invertDiff ::ttk::button .bb.save -text {Save As...} -command saveDiff pack .bb.quit .bb.invert -side left if {$fossilcmd!=""} {pack .bb.save -side left} pack .bb.files -side left grid rowconfigure . 1 -weight 1 grid columnconfigure . 1 -weight 1 grid columnconfigure . 4 -weight 1 grid .bb -row 0 -columnspan 6 eval grid [cols] -row 1 -sticky nsew grid .sby -row 1 -column 5 -sticky ns grid .sbxA -row 2 -columnspan 2 -sticky ew grid .spacer -row 2 -column 2 grid .sbxB -row 2 -column 3 -columnspan 2 -sticky ew .spacer config -height [winfo height .sbxA] wm deiconify . } eval $prog |
Changes to src/diffcmd.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 | */ #if defined(_WIN32) # define NULL_DEVICE "NUL" #else # define NULL_DEVICE "/dev/null" #endif /* ** Print the "Index:" message that patches wants to see at the top of a diff. */ void diff_print_index(const char *zFile, u64 diffFlags){ if( (diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF))==0 ){ char *z = mprintf("Index: %s\n%.66c\n", zFile, '='); fossil_print("%s", z); | > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | */ #if defined(_WIN32) # define NULL_DEVICE "NUL" #else # define NULL_DEVICE "/dev/null" #endif /* ** Used when the name for the diff is unknown. */ #define DIFF_NO_NAME "(unknown)" /* ** Print the "Index:" message that patches wants to see at the top of a diff. */ void diff_print_index(const char *zFile, u64 diffFlags){ if( (diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF))==0 ){ char *z = mprintf("Index: %s\n%.66c\n", zFile, '='); fossil_print("%s", z); |
︙ | ︙ | |||
47 48 49 50 51 52 53 54 | void diff_print_filenames(const char *zLeft, const char *zRight, u64 diffFlags){ char *z = 0; if( diffFlags & DIFF_BRIEF ){ /* no-op */ }else if( diffFlags & DIFF_SIDEBYSIDE ){ int w = diff_width(diffFlags); int n1 = strlen(zLeft); int x; | > > | | | | > > > > > > > > | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | void diff_print_filenames(const char *zLeft, const char *zRight, u64 diffFlags){ char *z = 0; if( diffFlags & DIFF_BRIEF ){ /* no-op */ }else if( diffFlags & DIFF_SIDEBYSIDE ){ int w = diff_width(diffFlags); int n1 = strlen(zLeft); int n2 = strlen(zRight); int x; if( n1==n2 && fossil_strcmp(zLeft,zRight)==0 ){ if( n1>w*2 ) n1 = w*2; x = w*2+17 - (n1+2); z = mprintf("%.*c %.*s %.*c\n", x/2, '=', n1, zLeft, (x+1)/2, '='); }else{ if( w<20 ) w = 20; if( n1>w-10 ) n1 = w - 10; if( n2>w-10 ) n2 = w - 10; z = mprintf("%.*c %.*s %.*c versus %.*c %.*s %.*c\n", (w-n1+10)/2, '=', n1, zLeft, (w-n1+1)/2, '=', (w-n2)/2, '=', n2, zRight, (w-n2+1)/2, '='); } }else{ z = mprintf("--- %s\n+++ %s\n", zLeft, zRight); } fossil_print("%s", z); fossil_free(z); } |
︙ | ︙ | |||
92 93 94 95 96 97 98 | const char *zName2; /* Name of zFile2 for display */ /* Read content of zFile2 into memory */ blob_zero(&file2); if( file_wd_size(zFile2)<0 ){ zName2 = NULL_DEVICE; }else{ | | | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | const char *zName2; /* Name of zFile2 for display */ /* Read content of zFile2 into memory */ blob_zero(&file2); if( file_wd_size(zFile2)<0 ){ zName2 = NULL_DEVICE; }else{ if( file_wd_islink(0) ){ blob_read_link(&file2, zFile2); }else{ blob_read_from_file(&file2, zFile2); } zName2 = zName; } |
︙ | ︙ | |||
125 126 127 128 129 130 131 | int cnt = 0; Blob nameFile1; /* Name of temporary file to old pFile1 content */ Blob cmd; /* Text of command to run */ if( !fIncludeBinary ){ Blob file2; if( isBin1 ){ | | | | | | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | int cnt = 0; Blob nameFile1; /* Name of temporary file to old pFile1 content */ Blob cmd; /* Text of command to run */ if( !fIncludeBinary ){ Blob file2; if( isBin1 ){ fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY); return; } if( zBinGlob ){ Glob *pBinary = glob_create(zBinGlob); if( glob_match(pBinary, zName) ){ fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY); glob_free(pBinary); return; } glob_free(pBinary); } blob_zero(&file2); if( file_wd_size(zFile2)>=0 ){ if( file_wd_islink(0) ){ blob_read_link(&file2, zFile2); }else{ blob_read_from_file(&file2, zFile2); } } if( looks_like_binary(&file2) ){ fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY); blob_reset(&file2); return; } blob_reset(&file2); } /* Construct a temporary file to hold pFile1 based on the name of ** zFile2 */ blob_zero(&nameFile1); do{ blob_reset(&nameFile1); blob_appendf(&nameFile1, "%s~%d", zFile2, cnt++); }while( file_access(blob_str(&nameFile1),F_OK)==0 ); blob_write_to_file(pFile1, blob_str(&nameFile1)); /* Construct the external diff command */ blob_zero(&cmd); blob_appendf(&cmd, "%s ", zDiffCmd); shell_escape(&cmd, blob_str(&nameFile1)); blob_append(&cmd, " ", 1); |
︙ | ︙ | |||
221 222 223 224 225 226 227 | }else{ Blob cmd; char zTemp1[300]; char zTemp2[300]; if( !fIncludeBinary ){ if( isBin1 || isBin2 ){ | | | | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | }else{ Blob cmd; char zTemp1[300]; char zTemp2[300]; if( !fIncludeBinary ){ if( isBin1 || isBin2 ){ fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY); return; } if( zBinGlob ){ Glob *pBinary = glob_create(zBinGlob); if( glob_match(pBinary, zName) ){ fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY); glob_free(pBinary); return; } glob_free(pBinary); } } |
︙ | ︙ | |||
285 286 287 288 289 290 291 | Blob content; int isLink; int isBin; file_tree_name(zFileTreeName, &fname, 1); historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, fIncludeBinary ? 0 : &isBin, 0); if( !isLink != !file_wd_islink(zFrom) ){ | | | 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | Blob content; int isLink; int isBin; file_tree_name(zFileTreeName, &fname, 1); historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, fIncludeBinary ? 0 : &isBin, 0); if( !isLink != !file_wd_islink(zFrom) ){ fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK); }else{ diff_file(&content, isBin, zFileTreeName, zFileTreeName, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); } blob_reset(&content); blob_reset(&fname); } |
︙ | ︙ | |||
329 330 331 332 333 334 335 | db_begin_transaction(); if( zFrom ){ int rid = name_to_typed_rid(zFrom, "ci"); if( !is_a_version(rid) ){ fossil_fatal("no such check-in: %s", zFrom); } load_vfile_from_rid(rid); | | | | | | | | | | | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | db_begin_transaction(); if( zFrom ){ int rid = name_to_typed_rid(zFrom, "ci"); if( !is_a_version(rid) ){ fossil_fatal("no such check-in: %s", zFrom); } load_vfile_from_rid(rid); blob_append_sql(&sql, "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid, v1.islink" " FROM vfile v1, vfile v2 " " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d" " AND (v2.deleted OR v2.chnged OR v1.mrid!=v2.rid)" "UNION " "SELECT pathname, 1, 0, 0, 0, islink" " FROM vfile v1" " WHERE v1.vid=%d" " AND NOT EXISTS(SELECT 1 FROM vfile v2" " WHERE v2.vid=%d AND v2.pathname=v1.pathname)" "UNION " "SELECT pathname, 0, 0, 1, 0, islink" " FROM vfile v2" " WHERE v2.vid=%d" " AND NOT EXISTS(SELECT 1 FROM vfile v1" " WHERE v1.vid=%d AND v1.pathname=v2.pathname)" " ORDER BY 1 /*scan*/", rid, vid, rid, vid, vid, rid ); }else{ blob_append_sql(&sql, "SELECT pathname, deleted, chnged , rid==0, rid, islink" " FROM vfile" " WHERE vid=%d" " AND (deleted OR chnged OR rid==0)" " ORDER BY pathname /*scan*/", vid ); } db_prepare(&q, "%s", blob_sql_text(&sql)); while( db_step(&q)==SQLITE_ROW ){ const char *zPathname = db_column_text(&q,0); int isDeleted = db_column_int(&q, 1); int isChnged = db_column_int(&q,2); int isNew = db_column_int(&q,3); int srcid = db_column_int(&q, 4); int isLink = db_column_int(&q, 5); char *zToFree = mprintf("%s%s", g.zLocalRoot, zPathname); const char *zFullName = zToFree; int showDiff = 1; if( isDeleted ){ fossil_print("DELETED %s\n", zPathname); if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; } }else if( file_access(zFullName, F_OK) ){ fossil_print("MISSING %s\n", zPathname); if( !asNewFile ){ showDiff = 0; } }else if( isNew ){ fossil_print("ADDED %s\n", zPathname); srcid = 0; if( !asNewFile ){ showDiff = 0; } }else if( isChnged==3 ){ fossil_print("ADDED_BY_MERGE %s\n", zPathname); srcid = 0; if( !asNewFile ){ showDiff = 0; } } if( showDiff ){ Blob content; int isBin; if( !isLink != !file_wd_islink(zFullName) ){ diff_print_index(zPathname, diffFlags); diff_print_filenames(zPathname, zPathname, diffFlags); fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK); continue; } if( srcid>0 ){ content_get(srcid, &content); }else{ blob_zero(&content); } |
︙ | ︙ | |||
445 446 447 448 449 450 451 | zName = blob_str(&fname); historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, fIncludeBinary ? 0 : &isBin1, 0); historical_version_of_file(zTo, zName, &v2, &isLink2, 0, fIncludeBinary ? 0 : &isBin2, 0); if( isLink1 != isLink2 ){ diff_print_filenames(zName, zName, diffFlags); | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | zName = blob_str(&fname); historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, fIncludeBinary ? 0 : &isBin1, 0); historical_version_of_file(zTo, zName, &v2, &isLink2, 0, fIncludeBinary ? 0 : &isBin2, 0); if( isLink1 != isLink2 ){ diff_print_filenames(zName, zName, diffFlags); fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK); }else{ diff_file_mem(&v1, &v2, isBin1, isBin2, zName, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); } blob_reset(&v1); blob_reset(&v2); blob_reset(&fname); |
︙ | ︙ | |||
477 478 479 480 481 482 483 | const char *zBinGlob, int fIncludeBinary, u64 diffFlags ){ Blob f1, f2; int isBin1, isBin2; int rid; | | > > > > > > > | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | const char *zBinGlob, int fIncludeBinary, u64 diffFlags ){ Blob f1, f2; int isBin1, isBin2; int rid; const char *zName; if( pFrom ){ zName = pFrom->zName; }else if( pTo ){ zName = pTo->zName; }else{ zName = DIFF_NO_NAME; } if( diffFlags & DIFF_BRIEF ) return; diff_print_index(zName, diffFlags); if( pFrom ){ rid = uuid_to_rid(pFrom->zUuid, 0); content_get(rid, &f1); }else{ blob_zero(&f1); |
︙ | ︙ | |||
593 594 595 596 597 598 599 | }else{ zDefault = 0; zName = "diff-command"; } return db_get(zName, zDefault); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > > | | | > > > > > > > > < < | < > | > | | | > | > > > > > > > > > > > > > > > > > > | | | | | > > | 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 | }else{ zDefault = 0; zName = "diff-command"; } return db_get(zName, zDefault); } /* ** Show diff output in a Tcl/Tk window, in response to the --tk option ** to the diff command. ** ** If fossil has direct access to a Tcl interpreter (either loaded ** dynamically through stubs or linked in statically), we can use it ** directly. Otherwise: ** (1) Write the Tcl/Tk script used for rendering into a temp file. ** (2) Invoke "tclsh" on the temp file using fossil_system(). ** (3) Delete the temp file. */ void diff_tk(const char *zSubCmd, int firstArg){ int i; Blob script; const char *zTempFile = 0; char *zCmd; blob_zero(&script); blob_appendf(&script, "set fossilcmd {| \"%/\" %s --html -y -i -v", g.nameOfExe, zSubCmd); find_option("html",0,0); find_option("side-by-side","y",0); find_option("internal","i",0); find_option("verbose","v",0); /* The undocumented --script FILENAME option causes the Tk script to ** be written into the FILENAME instead of being run. This is used ** for testing and debugging. */ zTempFile = find_option("script",0,1); for(i=firstArg; i<g.argc; i++){ const char *z = g.argv[i]; if( sqlite3_strglob("*}*",z) ){ blob_appendf(&script, " {%/}", z); }else{ int j; blob_append(&script, " ", 1); for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]); } } blob_appendf(&script, "}\n%s", builtin_file("diff.tcl", 0)); if( zTempFile ){ blob_write_to_file(&script, zTempFile); fossil_print("To see diff, run: tclsh \"%s\"\n", zTempFile); }else{ #if defined(FOSSIL_ENABLE_TCL) Th_FossilInit(TH_INIT_DEFAULT); if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script), blob_size(&script), 1, 0)==TCL_OK ){ blob_reset(&script); return; } /* * If evaluation of the Tcl script fails, the reason may be that Tk * could not be found by the loaded Tcl, or that Tcl cannot be loaded * dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback * to using the external "tclsh", if available. */ #endif zTempFile = write_blob_to_temp_file(&script); zCmd = mprintf("tclsh \"%s\"", zTempFile); fossil_system(zCmd); file_delete(zTempFile); fossil_free(zCmd); } blob_reset(&script); } /* ** Returns non-zero if files that may be binary should be used with external ** diff programs. */ int diff_include_binary_files(void){ |
︙ | ︙ | |||
972 973 974 975 976 977 978 | ** ** Show the difference between the current version of each of the FILEs ** specified (as they exist on disk) and that same file as it was checked ** out. Or if the FILE arguments are omitted, show the unsaved changed ** currently in the working check-out. ** ** If the "--from VERSION" or "-r VERSION" option is used it specifies | | | 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 | ** ** Show the difference between the current version of each of the FILEs ** specified (as they exist on disk) and that same file as it was checked ** out. Or if the FILE arguments are omitted, show the unsaved changed ** currently in the working check-out. ** ** If the "--from VERSION" or "-r VERSION" option is used it specifies ** the source check-in for the diff operation. If not specified, the ** source check-in is the base check-in for the current check-out. ** ** If the "--to VERSION" option appears, it specifies the check-in from ** which the second version of the file or files is taken. If there is ** no "--to" option then the (possibly edited) files in the current check-out ** are used. ** |
︙ | ︙ | |||
996 997 998 999 1000 1001 1002 | ** when using an external diff program. ** ** The "--binary" option causes files matching the glob PATTERN to be treated ** as binary when considering if they should be used with external diff program. ** This option overrides the "binary-glob" setting. ** ** Options: | | | | | | | | | > | | | | > | > | 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 | ** when using an external diff program. ** ** The "--binary" option causes files matching the glob PATTERN to be treated ** as binary when considering if they should be used with external diff program. ** This option overrides the "binary-glob" setting. ** ** Options: ** --binary PATTERN Treat files that match the glob PATTERN as binary ** --branch BRANCH Show diff of all changes on BRANCH ** --brief Show filenames only ** --context|-c N Use N lines of context ** --diff-binary BOOL Include binary files when using external commands ** --from|-r VERSION select VERSION as source for the diff ** --internal|-i use internal diff logic ** --side-by-side|-y side-by-side diff ** --strip-trailing-cr Strip trailing CR ** --tk Launch a Tcl/Tk GUI for display ** --to VERSION select VERSION as target for the diff ** --unified unified diff ** -v|--verbose output complete text of added or deleted files ** -w|--ignore-all-space Ignore white space when comparing lines ** -W|--width <num> Width of lines in side-by-side diff ** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace */ void diff_cmd(void){ int isGDiff; /* True for gdiff. False for normal diff */ int isInternDiff; /* True for internal diff */ int verboseFlag; /* True if -v or --verbose flag is used */ const char *zFrom; /* Source version number */ const char *zTo; /* Target version number */ |
︙ | ︙ | |||
1038 1039 1040 1041 1042 1043 1044 | zBranch = find_option("branch", 0, 1); diffFlags = diff_options(); verboseFlag = find_option("verbose","v",0)!=0; if( !verboseFlag ){ verboseFlag = find_option("new-file","N",0)!=0; /* deprecated */ } if( verboseFlag ) diffFlags |= DIFF_VERBOSE; | < | 785 786 787 788 789 790 791 792 793 794 795 796 797 798 | zBranch = find_option("branch", 0, 1); diffFlags = diff_options(); verboseFlag = find_option("verbose","v",0)!=0; if( !verboseFlag ){ verboseFlag = find_option("new-file","N",0)!=0; /* deprecated */ } if( verboseFlag ) diffFlags |= DIFF_VERBOSE; if( zBranch ){ if( zTo || zFrom ){ fossil_fatal("cannot use --from or --to with --branch"); } zTo = zBranch; zFrom = mprintf("root:%s", zBranch); } |
︙ | ︙ | |||
1093 1094 1095 1096 1097 1098 1099 | ** WEBPAGE: vpatch ** URL vpatch?from=UUID&to=UUID */ void vpatch_page(void){ const char *zFrom = P("from"); const char *zTo = P("to"); login_check_credentials(); | | | 839 840 841 842 843 844 845 846 847 848 849 850 851 | ** WEBPAGE: vpatch ** URL vpatch?from=UUID&to=UUID */ void vpatch_page(void){ const char *zFrom = P("from"); const char *zTo = P("to"); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( zFrom==0 || zTo==0 ) fossil_redirect_home(); cgi_set_content_type("text/plain"); diff_all_two_versions(zFrom, zTo, 0, 0, 0, DIFF_VERBOSE); } |
Changes to src/doc.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 | /* ** Try to guess the mimetype from content. ** ** If the content is pure text, return NULL. ** ** For image types, attempt to return an appropriate mimetype | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /* ** Try to guess the mimetype from content. ** ** If the content is pure text, return NULL. ** ** For image types, attempt to return an appropriate mimetype ** name like "image/gif" or "image/jpeg". ** ** For any other binary type, return "unknown/unknown". */ const char *mimetype_from_content(Blob *pBlob){ int i; int n; const unsigned char *x; |
︙ | ︙ | |||
63 64 65 66 67 68 69 | if( isBinary[c] ){ break; } } if( i>=n ){ return 0; /* Plain text */ } | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | if( isBinary[c] ){ break; } } if( i>=n ){ return 0; /* Plain text */ } for(i=0; i<ArraySize(aMime); i++){ if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){ return aMime[i].zMimetype; } } return "unknown/unknown"; } /* A table of mimetypes based on file suffixes. ** Suffixes must be in sorted order so that we can do a binary ** search to find the mime-type */ static const struct { const char *zSuffix; /* The file suffix */ int size; /* Length of the suffix */ const char *zMimetype; /* The corresponding mimetype */ } aMime[] = { { "ai", 2, "application/postscript" }, { "aif", 3, "audio/x-aiff" }, { "aifc", 4, "audio/x-aiff" }, { "aiff", 4, "audio/x-aiff" }, { "arj", 3, "application/x-arj-compressed" }, { "asc", 3, "text/plain" }, { "asf", 3, "video/x-ms-asf" }, { "asx", 3, "video/x-ms-asx" }, { "au", 2, "audio/ulaw" }, { "avi", 3, "video/x-msvideo" }, { "bat", 3, "application/x-msdos-program" }, { "bcpio", 5, "application/x-bcpio" }, { "bin", 3, "application/octet-stream" }, { "c", 1, "text/plain" }, { "cc", 2, "text/plain" }, { "ccad", 4, "application/clariscad" }, { "cdf", 3, "application/x-netcdf" }, { "class", 5, "application/octet-stream" }, { "cod", 3, "application/vnd.rim.cod" }, { "com", 3, "application/x-msdos-program" }, { "cpio", 4, "application/x-cpio" }, { "cpt", 3, "application/mac-compactpro" }, { "csh", 3, "application/x-csh" }, { "css", 3, "text/css" }, { "csv", 3, "text/csv" }, { "dcr", 3, "application/x-director" }, { "deb", 3, "application/x-debian-package" }, { "dir", 3, "application/x-director" }, { "dl", 2, "video/dl" }, { "dms", 3, "application/octet-stream" }, { "doc", 3, "application/msword" }, { "docx", 4, "application/vnd.openxmlformats-" "officedocument.wordprocessingml.document"}, { "dot", 3, "application/msword" }, { "dotx", 4, "application/vnd.openxmlformats-" "officedocument.wordprocessingml.template"}, { "drw", 3, "application/drafting" }, { "dvi", 3, "application/x-dvi" }, { "dwg", 3, "application/acad" }, { "dxf", 3, "application/dxf" }, { "dxr", 3, "application/x-director" }, { "eps", 3, "application/postscript" }, { "etx", 3, "text/x-setext" }, { "exe", 3, "application/octet-stream" }, { "ez", 2, "application/andrew-inset" }, { "f", 1, "text/plain" }, { "f90", 3, "text/plain" }, { "fli", 3, "video/fli" }, { "flv", 3, "video/flv" }, { "gif", 3, "image/gif" }, { "gl", 2, "video/gl" }, { "gtar", 4, "application/x-gtar" }, { "gz", 2, "application/x-gzip" }, { "h", 1, "text/plain" }, { "hdf", 3, "application/x-hdf" }, { "hh", 2, "text/plain" }, { "hqx", 3, "application/mac-binhex40" }, { "htm", 3, "text/html" }, { "html", 4, "text/html" }, { "ice", 3, "x-conference/x-cooltalk" }, { "ief", 3, "image/ief" }, { "iges", 4, "model/iges" }, { "igs", 3, "model/iges" }, { "ips", 3, "application/x-ipscript" }, { "ipx", 3, "application/x-ipix" }, { "jad", 3, "text/vnd.sun.j2me.app-descriptor" }, { "jar", 3, "application/java-archive" }, { "jpe", 3, "image/jpeg" }, { "jpeg", 4, "image/jpeg" }, { "jpg", 3, "image/jpeg" }, { "js", 2, "application/x-javascript" }, { "kar", 3, "audio/midi" }, { "latex", 5, "application/x-latex" }, { "lha", 3, "application/octet-stream" }, { "lsp", 3, "application/x-lisp" }, { "lzh", 3, "application/octet-stream" }, { "m", 1, "text/plain" }, { "m3u", 3, "audio/x-mpegurl" }, { "man", 3, "application/x-troff-man" }, { "markdown", 8, "text/x-markdown" }, { "md", 2, "text/x-markdown" }, { "me", 2, "application/x-troff-me" }, { "mesh", 4, "model/mesh" }, { "mid", 3, "audio/midi" }, { "midi", 4, "audio/midi" }, { "mif", 3, "application/x-mif" }, { "mime", 4, "www/mime" }, { "mkd", 3, "text/x-markdown" }, { "mov", 3, "video/quicktime" }, { "movie", 5, "video/x-sgi-movie" }, { "mp2", 3, "audio/mpeg" }, { "mp3", 3, "audio/mpeg" }, { "mp4", 3, "video/mp4" }, { "mpe", 3, "video/mpeg" }, { "mpeg", 4, "video/mpeg" }, { "mpg", 3, "video/mpeg" }, { "mpga", 4, "audio/mpeg" }, { "ms", 2, "application/x-troff-ms" }, { "msh", 3, "model/mesh" }, { "nc", 2, "application/x-netcdf" }, { "oda", 3, "application/oda" }, { "odp", 3, "application/vnd.oasis.opendocument.presentation" }, { "ods", 3, "application/vnd.oasis.opendocument.spreadsheet" }, { "odt", 3, "application/vnd.oasis.opendocument.text" }, { "ogg", 3, "application/ogg" }, { "ogm", 3, "application/ogg" }, { "pbm", 3, "image/x-portable-bitmap" }, { "pdb", 3, "chemical/x-pdb" }, { "pdf", 3, "application/pdf" }, { "pgm", 3, "image/x-portable-graymap" }, { "pgn", 3, "application/x-chess-pgn" }, { "pgp", 3, "application/pgp" }, { "pl", 2, "application/x-perl" }, { "pm", 2, "application/x-perl" }, { "png", 3, "image/png" }, { "pnm", 3, "image/x-portable-anymap" }, { "pot", 3, "application/mspowerpoint" }, { "potx", 4, "application/vnd.openxmlformats-" "officedocument.presentationml.template"}, { "ppm", 3, "image/x-portable-pixmap" }, { "pps", 3, "application/mspowerpoint" }, { "ppsx", 4, "application/vnd.openxmlformats-" "officedocument.presentationml.slideshow"}, { "ppt", 3, "application/mspowerpoint" }, { "pptx", 4, "application/vnd.openxmlformats-" "officedocument.presentationml.presentation"}, { "ppz", 3, "application/mspowerpoint" }, { "pre", 3, "application/x-freelance" }, { "prt", 3, "application/pro_eng" }, { "ps", 2, "application/postscript" }, { "qt", 2, "video/quicktime" }, { "ra", 2, "audio/x-realaudio" }, { "ram", 3, "audio/x-pn-realaudio" }, { "rar", 3, "application/x-rar-compressed" }, { "ras", 3, "image/cmu-raster" }, { "rgb", 3, "image/x-rgb" }, { "rm", 2, "audio/x-pn-realaudio" }, { "roff", 4, "application/x-troff" }, { "rpm", 3, "audio/x-pn-realaudio-plugin" }, { "rtf", 3, "text/rtf" }, { "rtx", 3, "text/richtext" }, { "scm", 3, "application/x-lotusscreencam" }, { "set", 3, "application/set" }, { "sgm", 3, "text/sgml" }, { "sgml", 4, "text/sgml" }, { "sh", 2, "application/x-sh" }, { "shar", 4, "application/x-shar" }, { "silo", 4, "model/mesh" }, { "sit", 3, "application/x-stuffit" }, { "skd", 3, "application/x-koan" }, { "skm", 3, "application/x-koan" }, { "skp", 3, "application/x-koan" }, { "skt", 3, "application/x-koan" }, { "smi", 3, "application/smil" }, { "smil", 4, "application/smil" }, { "snd", 3, "audio/basic" }, { "sol", 3, "application/solids" }, { "spl", 3, "application/x-futuresplash" }, { "src", 3, "application/x-wais-source" }, { "step", 4, "application/STEP" }, { "stl", 3, "application/SLA" }, { "stp", 3, "application/STEP" }, { "sv4cpio", 7, "application/x-sv4cpio" }, { "sv4crc", 6, "application/x-sv4crc" }, { "svg", 3, "image/svg+xml" }, { "swf", 3, "application/x-shockwave-flash" }, { "t", 1, "application/x-troff" }, { "tar", 3, "application/x-tar" }, { "tcl", 3, "application/x-tcl" }, { "tex", 3, "application/x-tex" }, { "texi", 4, "application/x-texinfo" }, { "texinfo", 7, "application/x-texinfo" }, { "tgz", 3, "application/x-tar-gz" }, { "th1", 3, "application/x-th1" }, { "tif", 3, "image/tiff" }, { "tiff", 4, "image/tiff" }, { "tr", 2, "application/x-troff" }, { "tsi", 3, "audio/TSP-audio" }, { "tsp", 3, "application/dsptype" }, { "tsv", 3, "text/tab-separated-values" }, { "txt", 3, "text/plain" }, { "unv", 3, "application/i-deas" }, { "ustar", 5, "application/x-ustar" }, { "vcd", 3, "application/x-cdlink" }, { "vda", 3, "application/vda" }, { "viv", 3, "video/vnd.vivo" }, { "vivo", 4, "video/vnd.vivo" }, { "vrml", 4, "model/vrml" }, { "wav", 3, "audio/x-wav" }, { "wax", 3, "audio/x-ms-wax" }, { "wiki", 4, "text/x-fossil-wiki" }, { "wma", 3, "audio/x-ms-wma" }, { "wmv", 3, "video/x-ms-wmv" }, { "wmx", 3, "video/x-ms-wmx" }, { "wrl", 3, "model/vrml" }, { "wvx", 3, "video/x-ms-wvx" }, { "xbm", 3, "image/x-xbitmap" }, { "xlc", 3, "application/vnd.ms-excel" }, { "xll", 3, "application/vnd.ms-excel" }, { "xlm", 3, "application/vnd.ms-excel" }, { "xls", 3, "application/vnd.ms-excel" }, { "xlsx", 4, "application/vnd.openxmlformats-" "officedocument.spreadsheetml.sheet"}, { "xlw", 3, "application/vnd.ms-excel" }, { "xml", 3, "text/xml" }, { "xpm", 3, "image/x-xpixmap" }, { "xwd", 3, "image/x-xwindowdump" }, { "xyz", 3, "chemical/x-pdb" }, { "zip", 3, "application/zip" }, }; /* ** Verify that all entries in the aMime[] table are in sorted order. ** Abort with a fatal error if any is out-of-order. */ static void mimetype_verify(void){ int i; for(i=1; i<ArraySize(aMime); i++){ if( fossil_strcmp(aMime[i-1].zSuffix,aMime[i].zSuffix)>=0 ){ fossil_fatal("mimetypes out of sequence: %s before %s", aMime[i-1].zSuffix, aMime[i].zSuffix); } } } /* ** Guess the mime-type of a document based on its name. */ const char *mimetype_from_name(const char *zName){ const char *z; int i; int first, last; int len; char zSuffix[20]; #ifdef FOSSIL_DEBUG /* This is test code to make sure the table above is in the correct ** order */ if( fossil_strcmp(zName, "mimetype-test")==0 ){ mimetype_verify(); return "ok"; } #endif z = zName; for(i=0; zName[i]; i++){ if( zName[i]=='.' ) z = &zName[i+1]; } len = strlen(z); if( len<sizeof(zSuffix)-1 ){ sqlite3_snprintf(sizeof(zSuffix), zSuffix, "%s", z); for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]); first = 0; last = ArraySize(aMime) - 1; while( first<=last ){ int c; i = (first+last)/2; c = fossil_strcmp(zSuffix, aMime[i].zSuffix); if( c==0 ) return aMime[i].zMimetype; if( c<0 ){ last = i-1; |
︙ | ︙ | |||
343 344 345 346 347 348 349 350 351 352 353 354 355 356 | ** ** If Fossil is compiled with -DFOSSIL_DEBUG then the "mimetype-test" ** filename is special and verifies the integrity of the mimetype table. ** It should return "ok". */ void mimetype_test_cmd(void){ int i; for(i=2; i<g.argc; i++){ fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i])); } } /* ** WEBPAGE: doc | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > | < | | > > > > > > > > > > > > > > > > > > | | > > > > | > > > | | | > | < | > | | > > | | > | > > | | | | | | | | | | | < < < | | | | | | < < | < > | < < | < < | < | < < < < < < < < < | | < < < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | > | < | > | > > > > > > > > > > > > > > > < > > | | > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 | ** ** If Fossil is compiled with -DFOSSIL_DEBUG then the "mimetype-test" ** filename is special and verifies the integrity of the mimetype table. ** It should return "ok". */ void mimetype_test_cmd(void){ int i; mimetype_verify(); for(i=2; i<g.argc; i++){ fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i])); } } /* ** WEBPAGE: mimetype_list ** ** Show the built-in table used to guess embedded document mimetypes ** from file suffixes. */ void mimetype_list_page(void){ int i; mimetype_verify(); style_header("Mimetype List"); @ <p>The Fossil <a href="%R/help?cmd=/doc">/doc</a> page uses filename @ suffixes and the following table to guess at the appropriate mimetype @ for each document.</p> @ <table id='mimeTable' border=1 cellpadding=0 class='mimetypetable'> @ <thead> @ <tr><th>Suffix<th>Mimetype @ </thead> @ <tbody> for(i=0; i<ArraySize(aMime); i++){ @ <tr><td>%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr> } @ </tbody></table> output_table_sorting_javascript("mimeTable","tt",1); style_footer(); } /* ** Check to see if the file in the pContent blob is "embedded HTML". Return ** true if it is, and fill pTitle with the document title. ** ** An "embedded HTML" file is HTML that lacks a header and a footer. The ** standard Fossil header is prepended and the standard Fossil footer is ** appended. Otherwise, the file is displayed without change. ** ** Embedded HTML must be contained in a <div class='fossil-doc'> element. ** If that <div> also contains a data-title attribute, then the ** value of that attribute is extracted into pTitle and becomes the title ** of the document. */ int doc_is_embedded_html(Blob *pContent, Blob *pTitle){ const char *zIn = blob_str(pContent); const char *zAttr; const char *zValue; int nAttr, nValue; int seenClass = 0; int seenTitle = 0; while( fossil_isspace(zIn[0]) ) zIn++; if( fossil_strnicmp(zIn,"<div",4)!=0 ) return 0; zIn += 4; while( zIn[0] ){ if( fossil_isspace(zIn[0]) ) zIn++; if( zIn[0]=='>' ) return 0; zAttr = zIn; while( fossil_isalnum(zIn[0]) || zIn[0]=='-' ) zIn++; nAttr = (int)(zIn - zAttr); while( fossil_isspace(zIn[0]) ) zIn++; if( zIn[0]!='=' ) continue; zIn++; while( fossil_isspace(zIn[0]) ) zIn++; if( zIn[0]=='"' || zIn[0]=='\'' ){ char cDelim = zIn[0]; zIn++; zValue = zIn; while( zIn[0] && zIn[0]!=cDelim ) zIn++; if( zIn[0]==0 ) return 0; nValue = (int)(zIn - zValue); zIn++; }else{ zValue = zIn; while( zIn[0]!=0 && zIn[0]!='>' && zIn[0]!='/' && !fossil_isspace(zIn[0]) ) zIn++; if( zIn[0]==0 ) return 0; nValue = (int)(zIn - zValue); } if( nAttr==5 && fossil_strnicmp(zAttr,"class",5)==0 ){ if( nValue!=10 || fossil_strnicmp(zValue,"fossil-doc",10)!=0 ) return 0; seenClass = 1; if( seenTitle ) return 1; } if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){ blob_append(pTitle, zValue, nValue); seenTitle = 1; if( seenClass ) return 1; } } return seenClass; } /* ** Look for a file named zName in the check-in with RID=vid. Load the content ** of that file into pContent and return the RID for the file. Or return 0 ** if the file is not found or could not be loaded. */ int doc_load_content(int vid, const char *zName, Blob *pContent){ int rid; /* The RID of the file being loaded */ if( !db_table_exists("repository","vcache") ){ db_multi_exec( "CREATE TABLE IF NOT EXISTS vcache(\n" " vid INTEGER, -- check-in ID\n" " fname TEXT, -- filename\n" " rid INTEGER, -- artifact ID\n" " PRIMARY KEY(vid,fname)\n" ") WITHOUT ROWID" ); } if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ db_multi_exec( "DELETE FROM vcache;\n" "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;\n" "INSERT INTO vcache(vid,fname,rid)" " SELECT checkinID, filename, blob.rid FROM foci, blob" " WHERE blob.uuid=foci.uuid" " AND foci.checkinID=%d;", vid ); } rid = db_int(0, "SELECT rid FROM vcache" " WHERE vid=%d AND fname=%Q", vid, zName); if( rid && content_get(rid, pContent)==0 ){ rid = 0; } return rid; } /* ** WEBPAGE: doc ** URL: /doc?name=CHECKIN/FILE ** URL: /doc/CHECKIN/FILE ** ** CHECKIN can be either tag or SHA1 hash or timestamp identifying a ** particular check, or the name of a branch (meaning the most recent ** check-in on that branch) or one of various magic words: ** ** "tip" means the most recent check-in ** ** "ckout" means the current check-out, if the server is run from ** within a check-out, otherwise it is the same as "tip" ** ** FILE is the name of a file to delivered up as a webpage. FILE is relative ** to the root of the source tree of the repository. The FILE must ** be a part of CHECKIN, except when CHECKIN=="ckout" when FILE is read ** directly from disk and need not be a managed file. ** ** The "ckout" CHECKIN is intended for development - to provide a mechanism ** for looking at what a file will look like using the /doc webpage after ** it gets checked in. ** ** The file extension is used to decide how to render the file. ** ** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki", ** and "FILE/index.md" are in that order. If none of those are found, ** then FILE is completely replaced by "404.md" and tried. If that is ** not found, then a default 404 screen is generated. */ void doc_page(void){ const char *zName; /* Argument to the /doc page */ const char *zOrigName = "?"; /* Original document name */ const char *zMime; /* Document MIME type */ char *zCheckin = "tip"; /* The check-in holding the document */ int vid = 0; /* Artifact of check-in */ int rid = 0; /* Artifact of file */ int i; /* Loop counter */ Blob filebody; /* Content of the documentation file */ Blob title; /* Document title */ int nMiss = (-1); /* Failed attempts to find the document */ static const char *const azSuffix[] = { "index.html", "index.wiki", "index.md" }; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } blob_init(&title, 0, 0); db_begin_transaction(); while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){ zName = PD("name", "tip/index.wiki"); for(i=0; zName[i] && zName[i]!='/'; i++){} zCheckin = mprintf("%.*s", i, zName); if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ zCheckin = "tip"; } if( nMiss==ArraySize(azSuffix) ){ zName = "404.md"; }else if( zName[i]==0 ){ assert( nMiss>=0 && nMiss<ArraySize(azSuffix) ); zName = azSuffix[nMiss]; }else{ zName += i; } while( zName[0]=='/' ){ zName++; } g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName); if( nMiss==0 ) zOrigName = zName; if( !file_is_simple_pathname(zName, 1) ){ if( sqlite3_strglob("*/", zName)==0 ){ assert( nMiss>=0 && nMiss<ArraySize(azSuffix) ); zName = mprintf("%s%s", zName, azSuffix[nMiss]); if( !file_is_simple_pathname(zName, 1) ){ goto doc_not_found; } }else{ goto doc_not_found; } } if( fossil_strcmp(zCheckin,"ckout")==0 ){ /* Read from the local checkout */ char *zFullpath; db_must_be_within_tree(); zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); if( file_isfile(zFullpath) && blob_read_from_file(&filebody, zFullpath)>0 ){ rid = 1; /* Fake RID just to get the loop to end */ } fossil_free(zFullpath); }else{ vid = name_to_typed_rid(zCheckin, "ci"); rid = doc_load_content(vid, zName, &filebody); } } if( rid==0 ) goto doc_not_found; blob_to_utf8_no_bom(&filebody, 0); /* The file is now contained in the filebody blob. Deliver the ** file to the user */ zMime = nMiss==0 ? P("mimetype") : 0; if( zMime==0 ){ zMime = mimetype_from_name(zName); } Th_Store("doc_name", zName); Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" " FROM blob WHERE rid=%d", vid)); Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" " WHERE objid=%d AND type='ci'", vid)); if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ Blob tail; style_adunit_config(ADUNIT_RIGHT_OK); if( wiki_find_title(&filebody, &title, &tail) ){ style_header("%s", blob_str(&title)); wiki_convert(&tail, 0, WIKI_BUTTONS); }else{ style_header("Documentation"); wiki_convert(&filebody, 0, WIKI_BUTTONS); } style_footer(); }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ Blob tail = BLOB_INITIALIZER; markdown_to_html(&filebody, &title, &tail); if( blob_size(&title)>0 ){ style_header("%s", blob_str(&title)); }else{ style_header("%s", nMiss>=ArraySize(azSuffix)? "Not Found" : "Documentation"); } blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail)); style_footer(); }else if( fossil_strcmp(zMime, "text/plain")==0 ){ style_header("Documentation"); @ <blockquote><pre> @ %h(blob_str(&filebody)) @ </pre></blockquote> style_footer(); }else if( fossil_strcmp(zMime, "text/html")==0 && doc_is_embedded_html(&filebody, &title) ){ if( blob_size(&title)==0 ) blob_append(&title,zName,-1); style_header("%s", blob_str(&title)); blob_append(cgi_output_blob(), blob_buffer(&filebody),blob_size(&filebody)); style_footer(); #ifdef FOSSIL_ENABLE_TH1_DOCS }else if( Th_AreDocsEnabled() && fossil_strcmp(zMime, "application/x-th1")==0 ){ style_header("%h", zName); Th_Render(blob_str(&filebody)); style_footer(); #endif }else{ cgi_set_content_type(zMime); cgi_set_content(&filebody); } if( nMiss>=ArraySize(azSuffix) ) cgi_set_status(404, "Not Found"); db_end_transaction(0); return; /* Jump here when unable to locate the document */ doc_not_found: db_end_transaction(0); cgi_set_status(404, "Not Found"); style_header("Not Found"); @ <p>Document %h(zOrigName) not found if( fossil_strcmp(zCheckin,"ckout")!=0 ){ @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> } style_footer(); db_end_transaction(0); return; } /* ** The default logo. */ static const unsigned char aLogo[] = { 71, 73, 70, 56, 55, 97, 62, 0, 71, 0, 244, 0, 0, 85, 129, 149, 95, 136, 155, 99, 139, 157, 106, 144, 162, 113, 150, 166, 116, 152, 168, 127, 160, 175, 138, 168, 182, 148, 176, 188, 159, 184, 195, 170, 192, 202, 180, 199, 208, 184, 202, 210, 191, 207, 215, 201, 215, 221, 212, 223, 228, 223, 231, 235, 226, 227, 226, 226, 234, 237, 233, 239, 241, 240, 244, 246, 244, 247, 248, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 62, 0, 71, 0, 0, 5, 255, 96, 100, 141, 100, 105, 158, 168, 37, 41, 132, 192, 164, 112, 44, 207, 102, 99, 0, 56, 16, 84, 116, 239, 199, 141, 65, 110, 232, 248, 25, 141, 193, 161, 82, 113, 108, 202, 32, 55, 229, 210, 73, 61, 41, 164, 88, 102, 181, 10, 41, 96, 179, 91, 106, 35, 240, 5, 135, 143, 137, 242, 87, 123, 246, 33, 190, 81, 108, 163, 237, 198, 14, 30, 113, 233, 131, 78, 115, 72, 11, 115, 87, 101, 19, 124, 51, 66, 74, 8, 19, 16, 67, 100, 74, 133, 50, 15, 101, 135, 56, 11, 74, 6, 143, 49, 126, 106, 56, 8, 145, 67, 9, 152, 48, 139, 155, 5, 22, 13, 74, 115, 161, 41, 147, 101, 13, 130, 57, 132, 170, 40, 167, 155, 0, 94, 57, 3, 178, 48, 183, 181, 57, 160, 186, 40, 19, 141, 189, 0, 69, 192, 40, 16, 195, 155, 185, 199, 41, 201, 189, 191, 205, 193, 188, 131, 210, 49, 175, 88, 209, 214, 38, 19, 3, 11, 19, 111, 127, 60, 219, 39, 55, 204, 19, 11, 6, 100, 5, 10, 227, 228, 37, 163, 0, 239, 117, 56, 238, 243, 49, 195, 177, 247, 48, 158, 56, 251, 50, 216, 254, 197, 56, 128, 107, 158, 2, 125, 171, 114, 92, 218, 246, 96, 66, 3, 4, 50, 134, 176, 145, 6, 97, 64, 144, 24, 19, 136, 108, 91, 177, 160, 0, 194, 19, 253, 0, 216, 107, 214, 224, 192, 129, 5, 16, 83, 255, 244, 43, 213, 195, 24, 159, 27, 169, 64, 230, 88, 208, 227, 129, 182, 54, 4, 89, 158, 24, 181, 163, 199, 1, 155, 52, 233, 8, 130, 176, 83, 24, 128, 137, 50, 18, 32, 48, 48, 114, 11, 173, 137, 19, 110, 4, 64, 105, 1, 194, 30, 140, 68, 15, 24, 24, 224, 50, 76, 70, 0, 11, 171, 54, 26, 160, 181, 194, 149, 148, 40, 174, 148, 122, 64, 180, 208, 161, 17, 207, 112, 164, 1, 128, 96, 148, 78, 18, 21, 194, 33, 229, 51, 247, 65, 133, 97, 5, 250, 69, 229, 100, 34, 220, 128, 166, 116, 190, 62, 8, 167, 195, 170, 47, 163, 0, 130, 90, 152, 11, 160, 173, 170, 27, 154, 26, 91, 232, 151, 171, 18, 14, 162, 253, 98, 170, 18, 70, 171, 64, 219, 10, 67, 136, 134, 187, 116, 75, 180, 46, 179, 174, 135, 4, 189, 229, 231, 78, 40, 10, 62, 226, 164, 172, 64, 240, 167, 170, 10, 18, 124, 188, 10, 107, 65, 193, 94, 11, 93, 171, 28, 248, 17, 239, 46, 140, 78, 97, 34, 25, 153, 36, 99, 65, 130, 7, 203, 183, 168, 51, 34, 136, 25, 140, 10, 6, 16, 28, 255, 145, 241, 230, 140, 10, 66, 178, 167, 112, 48, 192, 128, 129, 9, 31, 141, 84, 138, 63, 163, 162, 2, 203, 206, 240, 56, 55, 98, 192, 188, 15, 185, 50, 160, 6, 0, 125, 62, 33, 214, 195, 33, 5, 24, 184, 25, 231, 14, 201, 245, 144, 23, 126, 104, 228, 0, 145, 2, 13, 140, 244, 212, 17, 21, 20, 176, 159, 17, 95, 225, 160, 128, 16, 1, 32, 224, 142, 32, 227, 125, 87, 64, 0, 16, 54, 129, 205, 2, 141, 76, 53, 130, 103, 37, 166, 64, 144, 107, 78, 196, 5, 192, 0, 54, 50, 229, 9, 141, 49, 84, 194, 35, 12, 196, 153, 48, 192, 137, 57, 84, 24, 7, 87, 159, 249, 240, 215, 143, 105, 241, 118, 149, 9, 139, 4, 64, 203, 141, 35, 140, 129, 131, 16, 222, 125, 231, 128, 2, 238, 17, 152, 66, 3, 5, 56, 224, 159, 103, 16, 76, 25, 75, 5, 11, 164, 215, 96, 9, 14, 16, 36, 225, 15, 11, 40, 144, 192, 156, 41, 10, 178, 199, 3, 66, 64, 80, 193, 3, 124, 90, 48, 129, 129, 102, 177, 18, 192, 154, 49, 84, 240, 208, 92, 22, 149, 96, 39, 9, 31, 74, 17, 94, 3, 8, 177, 199, 72, 59, 85, 76, 25, 216, 8, 139, 194, 197, 138, 163, 69, 96, 115, 0, 147, 72, 72, 84, 28, 14, 79, 86, 233, 230, 23, 113, 26, 160, 128, 3, 10, 58, 129, 103, 14, 159, 214, 163, 146, 117, 238, 213, 154, 128, 151, 109, 84, 64, 217, 13, 27, 10, 228, 39, 2, 235, 164, 168, 74, 8, 0, 59, }; /* ** WEBPAGE: logo ** ** Return the logo image. This image is available to anybody who can see ** the login page. It is designed for use in the upper left-hand corner |
︙ | ︙ | |||
658 659 660 661 662 663 664 | if( blob_size(&bgimg)==0 ){ blob_init(&bgimg, (char*)aBackground, sizeof(aBackground)); } cgi_set_content_type(zMime); cgi_set_content(&bgimg); g.isConst = 1; } | > > > > > > > > > > > > > | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 | if( blob_size(&bgimg)==0 ){ blob_init(&bgimg, (char*)aBackground, sizeof(aBackground)); } cgi_set_content_type(zMime); cgi_set_content(&bgimg); g.isConst = 1; } /* ** WEBPAGE: /docsrch ** ** Search for documents that match a user-supplied pattern. */ void doc_search_page(void){ login_check_credentials(); style_header("Document Search"); search_screen(SRCH_DOC, 0); style_footer(); } |
Changes to src/encode.c.
︙ | ︙ | |||
45 46 47 48 49 50 51 | } i++; } i = 0; zOut = fossil_malloc( count+1 ); while( n-->0 && (c = *zIn)!=0 ){ switch( c ){ | | | | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | } i++; } i = 0; zOut = fossil_malloc( count+1 ); while( n-->0 && (c = *zIn)!=0 ){ switch( c ){ case '<': zOut[i++] = '&'; zOut[i++] = 'l'; zOut[i++] = 't'; zOut[i++] = ';'; break; case '>': zOut[i++] = '&'; zOut[i++] = 'g'; zOut[i++] = 't'; zOut[i++] = ';'; break; case '&': zOut[i++] = '&'; zOut[i++] = 'a'; zOut[i++] = 'm'; zOut[i++] = 'p'; zOut[i++] = ';'; break; case '"': zOut[i++] = '&'; zOut[i++] = 'q'; zOut[i++] = 'u'; zOut[i++] = 'o'; zOut[i++] = 't'; zOut[i++] = ';'; break; |
︙ | ︙ | |||
179 180 181 182 183 184 185 | } /* ** Convert the input string into a form that is suitable for use as ** a token in the HTTP protocol. Spaces are encoded as '+' and special ** characters are encoded as "%HH" where HH is a two-digit hexidecimal ** representation of the character. The "/" character is not encoded | | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | } /* ** Convert the input string into a form that is suitable for use as ** a token in the HTTP protocol. Spaces are encoded as '+' and special ** characters are encoded as "%HH" where HH is a two-digit hexidecimal ** representation of the character. The "/" character is not encoded ** by this routine. */ char *urlize(const char *z, int n){ return EncodeHttp(z, n, 0); } /* ** Convert a single HEX digit to an integer |
︙ | ︙ | |||
325 326 327 328 329 330 331 | if( z[j] ) z[j] = 0; } /* ** The characters used for HTTP base64 encoding. */ | | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | if( z[j] ) z[j] = 0; } /* ** The characters used for HTTP base64 encoding. */ static unsigned char zBase[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* ** Encode a string using a base-64 encoding. ** The encoding can be reversed using the <b>decode64</b> function. ** ** Space to hold the result comes from malloc(). |
︙ | ︙ | |||
364 365 366 367 368 369 370 | z64[n++] = '='; } z64[n] = 0; return z64; } /* | | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | z64[n++] = '='; } z64[n] = 0; return z64; } /* ** COMMAND: test-encode64 ** Usage: %fossil test-encode64 STRING */ void test_encode64_cmd(void){ char *z; int i; for(i=2; i<g.argc; i++){ z = encode64(g.argv[i], -1); |
︙ | ︙ | |||
429 430 431 432 433 434 435 | } zData[j] = 0; *pnByte = j; return zData; } /* | | | 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | } zData[j] = 0; *pnByte = j; return zData; } /* ** COMMAND: test-decode64 ** Usage: %fossil test-decode64 STRING */ void test_decode64_cmd(void){ char *z; int i, n; for(i=2; i<g.argc; i++){ z = decode64(g.argv[i], &n); |
︙ | ︙ | |||
452 453 454 455 456 457 458 | ** 0123456789abcdef ** */ /* ** The array used for encoding */ /* 123456789 12345 */ | | | | | | | | | | | | | | | | | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | ** 0123456789abcdef ** */ /* ** The array used for encoding */ /* 123456789 12345 */ static const char zEncode[] = "0123456789abcdef"; /* ** Encode a N-digit base-256 in base-16. Return zero on success ** and non-zero if there is an error. */ int encode16(const unsigned char *pIn, unsigned char *zOut, int N){ int i; for(i=0; i<N; i++){ *(zOut++) = zEncode[pIn[i]>>4]; *(zOut++) = zEncode[pIn[i]&0xf]; } *zOut = 0; return 0; } /* ** An array for translating single base-16 characters into a value. ** Disallowed input characters have a value of 64. Upper and lower ** case is the same. */ static const char zDecode[] = { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 64, 64, 64, 64, 64, 64, 64, 10, 11, 12, 13, 14, 15, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 10, 11, 12, 13, 14, 15, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, }; /* ** Decode a N-character base-16 number into base-256. N must be a ** multiple of 2. The output buffer must be at least N/2 characters ** in length */ int decode16(const unsigned char *zIn, unsigned char *pOut, int N){ int i, j; if( (N&1)!=0 ) return 1; for(i=j=0; i<N; i += 2, j++){ |
︙ | ︙ | |||
543 544 545 546 547 548 549 | } /* Randomness used for XOR-ing by the obscure() and unobscure() routines */ static const unsigned char aObscurer[16] = { 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 }; | | | | | | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 | } /* Randomness used for XOR-ing by the obscure() and unobscure() routines */ static const unsigned char aObscurer[16] = { 0xa7, 0x21, 0x31, 0xe3, 0x2a, 0x50, 0x2c, 0x86, 0x4c, 0xa4, 0x52, 0x25, 0xff, 0x49, 0x35, 0x85 }; /* ** Obscure plain text so that it is not easily readable. ** ** This is used for storing sensitive information (such as passwords) in a ** way that prevents their exposure through idle browsing. This is not ** encryption. Anybody who really wants the password can still get it. ** ** The text is XOR-ed with a repeating pattern then converted to hex. ** Space to hold the returned string is obtained from malloc and should ** be freed by the caller. */ char *obscure(const char *zIn){ int n, i; unsigned char salt; char *zOut; if( zIn==0 ) return 0; n = strlen(zIn); zOut = fossil_malloc( n*2+3 ); sqlite3_randomness(1, &salt); zOut[n+1] = (char)salt; for(i=0; i<n; i++) zOut[i+n+2] = zIn[i]^aObscurer[i&0x0f]^salt; encode16((unsigned char*)&zOut[n+1], (unsigned char*)zOut, n+1); return zOut; } /* ** Undo the obscuring of text performed by obscure(). Or, if the input is ** not hexadecimal (meaning the input is not the output of obscure()) then ** do the equivalent of strdup(). ** ** The result is memory obtained from malloc that should be freed by the caller. */ char *unobscure(const char *zIn){ int n, i; unsigned char salt; char *zOut; if( zIn==0 ) return 0; n = strlen(zIn); zOut = fossil_malloc( n + 1 ); if( n<2 || decode16((unsigned char*)zIn, &salt, 2) || decode16((unsigned char*)&zIn[2], (unsigned char*)zOut, n-2) ){ |
︙ | ︙ |
Changes to src/event.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to do formatting of event messages: ** ** Milestones ** Blog posts ** New articles ** Process checkpoints ** Announcements */ #include <assert.h> #include <ctype.h> | > > > > > > > > > < | | | | | > | > > | | | | | | | | | | > | | | | | | | | | | > | > > > | > > > > > | > > | > | < | | < | > | | < | | < | | < | | | | | | | | | > | > > > > > > > | | | | > > > > > | < > > | | | > | | | | | > > > > > > | | > > | > | | | | | | > | | > > > | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to do formatting of event messages: ** ** Technical Notes ** Milestones ** Blog posts ** New articles ** Process checkpoints ** Announcements ** ** Do not confuse "event" artifacts with the "event" table in the ** repository database. An "event" artifact is a technical-note: a ** wiki- or blog-like essay that appears on the timeline. The "event" ** table records all entries on the timeline, including tech-notes. ** ** (2015-02-14): Changing the name to "tech-note" most everywhere. */ #include "config.h" #include <assert.h> #include <ctype.h> #include "event.h" /* ** Output a hyperlink to an technote given its tagid. */ void hyperlink_to_event_tagid(int tagid){ char *zId; zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d", tagid); @ [%z(href("%R/technote/%s",zId))%S(zId)</a>] free(zId); } /* ** WEBPAGE: technote ** WEBPAGE: event ** ** Display a "technical note" or "tech-note" (formerly called an "event"). ** ** PARAMETERS: ** ** name=ID // Identify the tech-note to display. ID must be complete ** aid=ARTIFACTID // Which specific version of the tech-note. Optional. ** v=BOOLEAN // Show details if TRUE. Default is FALSE. Optional. ** ** Display an existing event identified by EVENTID */ void event_page(void){ int rid = 0; /* rid of the event artifact */ char *zUuid; /* UUID corresponding to rid */ const char *zId; /* Event identifier */ const char *zVerbose; /* Value of verbose option */ char *zETime; /* Time of the tech-note */ char *zATime; /* Time the artifact was created */ int specRid; /* rid specified by aid= parameter */ int prevRid, nextRid; /* Previous or next edits of this tech-note */ Manifest *pTNote; /* Parsed technote artifact */ Blob fullbody; /* Complete content of the technote body */ Blob title; /* Title extracted from the technote body */ Blob tail; /* Event body that comes after the title */ Stmt q1; /* Query to search for the technote */ int verboseFlag; /* True to show details */ const char *zMimetype = 0; /* Mimetype of the document */ /* wiki-read privilege is needed in order to read tech-notes. */ login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } zId = P("name"); if( zId==0 ){ fossil_redirect_home(); return; } zUuid = (char*)P("aid"); specRid = zUuid ? uuid_to_rid(zUuid, 0) : 0; rid = nextRid = prevRid = 0; db_prepare(&q1, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB 'event-%q*')" " ORDER BY mtime DESC", zId ); while( db_step(&q1)==SQLITE_ROW ){ nextRid = rid; rid = db_column_int(&q1, 0); if( specRid==0 || specRid==rid ){ if( db_step(&q1)==SQLITE_ROW ){ prevRid = db_column_int(&q1, 0); } break; } } db_finalize(&q1); if( rid==0 || (specRid!=0 && specRid!=rid) ){ style_header("No Such Tech-Note"); @ Cannot locate a technical note called <b>%h(zId)</b>. style_footer(); return; } zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); zVerbose = P("v"); if( !zVerbose ){ zVerbose = P("verbose"); } if( !zVerbose ){ zVerbose = P("detail"); /* deprecated */ } verboseFlag = (zVerbose!=0) && !is_false(zVerbose); /* Extract the event content. */ pTNote = manifest_get(rid, CFTYPE_EVENT, 0); if( pTNote==0 ){ fossil_fatal("Object #%d is not a tech-note", rid); } zMimetype = wiki_filter_mimetypes(PD("mimetype",pTNote->zMimetype)); blob_init(&fullbody, pTNote->zWiki, -1); blob_init(&title, 0, 0); blob_init(&tail, 0, 0); if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ if( !wiki_find_title(&fullbody, &title, &tail) ){ blob_appendf(&title, "Tech-note %S", zId); tail = fullbody; } }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ markdown_to_html(&fullbody, &title, &tail); if( blob_size(&title)==0 ){ blob_appendf(&title, "Tech-note %S", zId); } }else{ blob_appendf(&title, "Tech-note %S", zId); tail = fullbody; } style_header("%s", blob_str(&title)); if( g.perm.WrWiki && g.perm.Write && nextRid==0 ){ style_submenu_element("Edit", 0, "%R/technoteedit?name=%!S", zId); } zETime = db_text(0, "SELECT datetime(%.17g)", pTNote->rEventDate); style_submenu_element("Context", 0, "%R/timeline?c=%.20s", zId); if( g.perm.Hyperlink ){ if( verboseFlag ){ style_submenu_element("Plain", 0, "%R/technote?name=%!S&aid=%s&mimetype=text/plain", zId, zUuid); if( nextRid ){ char *zNext; zNext = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nextRid); style_submenu_element("Next", 0,"%R/technote?name=%!S&aid=%s&v", zId, zNext); free(zNext); } if( prevRid ){ char *zPrev; zPrev = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", prevRid); style_submenu_element("Prev", 0, "%R/technote?name=%!S&aid=%s&v", zId, zPrev); free(zPrev); } }else{ style_submenu_element("Detail", 0, "%R/technote?name=%!S&aid=%s&v", zId, zUuid); } } if( verboseFlag && g.perm.Hyperlink ){ int i; const char *zClr = 0; Blob comment; zATime = db_text(0, "SELECT datetime(%.17g)", pTNote->rDate); @ <p>Tech-note [%z(href("%R/artifact/%!S",zUuid))%S(zUuid)</a>] at @ [%z(href("%R/timeline?c=%T",zETime))%s(zETime)</a>] @ entered by user <b>%h(pTNote->zUser)</b> on @ [%z(href("%R/timeline?c=%T",zATime))%s(zATime)</a>]:</p> @ <blockquote> for(i=0; i<pTNote->nTag; i++){ if( fossil_strcmp(pTNote->aTag[i].zName,"+bgcolor")==0 ){ zClr = pTNote->aTag[i].zValue; } } if( zClr && zClr[0]==0 ) zClr = 0; if( zClr ){ @ <div style="background-color: %h(zClr);"> }else{ @ <div> } blob_init(&comment, pTNote->zComment, -1); wiki_convert(&comment, 0, WIKI_INLINE); blob_reset(&comment); @ </div> @ </blockquote><hr /> } if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ wiki_convert(&fullbody, 0, 0); }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ cgi_append_content(blob_buffer(&tail), blob_size(&tail)); }else{ @ <pre> @ %h(blob_str(&fullbody)) @ </pre> } style_footer(); manifest_destroy(pTNote); } /* ** WEBPAGE: technoteedit ** WEBPAGE: eventedit ** ** Revise or create a technical note (formerly called an 'event'). ** ** Parameters: ** ** name=ID Hex hash ID of the tech-note. If omitted, a new ** tech-note is created. */ void eventedit_page(void){ char *zTag; int rid = 0; Blob event; const char *zId; int n; const char *z; char *zBody = (char*)P("w"); char *zETime = (char*)P("t"); const char *zComment = P("c"); const char *zTags = P("g"); const char *zClr; const char *zMimetype = P("mimetype"); int isNew = 0; if( zBody ){ zBody = mprintf("%s", zBody); } login_check_credentials(); zId = P("name"); if( zId==0 ){ zId = db_text(0, "SELECT lower(hex(randomblob(20)))"); isNew = 1; }else{ int nId = strlen(zId); if( !validate16(zId, nId) ){ fossil_redirect_home(); return; } } zTag = mprintf("event-%s", zId); rid = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname GLOB '%q*')" " ORDER BY mtime DESC", zTag ); if( rid && strlen(zId)<40 ){ zId = db_text(0, "SELECT substr(tagname,7) FROM tag WHERE tagname GLOB '%q*'", zTag ); } free(zTag); /* Need both check-in and wiki-write or wiki-create privileges in order ** to edit/create an event. */ if( !g.perm.Write || (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){ login_needed(g.anon.Write && (rid ? g.anon.WrWiki : g.anon.NewWiki)); return; } /* Figure out the color */ if( rid ){ zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid); }else{ zClr = ""; isNew = 1; } zClr = PD("clr",zClr); if( fossil_strcmp(zClr,"##")==0 ) zClr = PD("cclr",""); /* If editing an existing event, extract the key fields to use as ** a starting point for the edit. */ if( rid && (zBody==0 || zETime==0 || zComment==0 || zTags==0 || zMimetype==0) ){ Manifest *pTNote; pTNote = manifest_get(rid, CFTYPE_EVENT, 0); if( pTNote && pTNote->type==CFTYPE_EVENT ){ if( zBody==0 ) zBody = pTNote->zWiki; if( zETime==0 ){ zETime = db_text(0, "SELECT datetime(%.17g)", pTNote->rEventDate); } if( zComment==0 ) zComment = pTNote->zComment; if( zMimetype==0 ) zMimetype = pTNote->zMimetype; } if( zTags==0 ){ zTags = db_text(0, "SELECT group_concat(substr(tagname,5),', ')" " FROM tagxref, tag" " WHERE tagxref.rid=%d" " AND tagxref.tagid=tag.tagid" " AND tag.tagname GLOB 'sym-*'", rid ); } } zETime = db_text(0, "SELECT coalesce(datetime(%Q),datetime('now'))", zETime); if( P("submit")!=0 && (zBody!=0 && zComment!=0) ){ char *zDate; Blob cksum; int nrid, n; blob_init(&event, 0, 0); db_begin_transaction(); login_verify_csrf_secret(); while( fossil_isspace(zComment[0]) ) zComment++; n = strlen(zComment); while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; } if( n>0 ){ blob_appendf(&event, "C %#F\n", n, zComment); } zDate = date_in_standard_format("now"); blob_appendf(&event, "D %s\n", zDate); free(zDate); zETime[10] = 'T'; blob_appendf(&event, "E %s %s\n", zETime, zId); zETime[10] = ' '; if( rid ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); blob_appendf(&event, "P %s\n", zUuid); free(zUuid); } if( zMimetype && zMimetype[0] ){ blob_appendf(&event, "N %s\n", zMimetype); } if( zClr && zClr[0] ){ blob_appendf(&event, "T +bgcolor * %F\n", zClr); } if( zTags && zTags[0] ){ Blob tags, one; int i, j; Stmt q; char *zBlob; /* Load the tags string into a blob */ blob_zero(&tags); blob_append(&tags, zTags, -1); /* Collapse all sequences of whitespace and "," characters into ** a single space character */ zBlob = blob_str(&tags); for(i=j=0; zBlob[i]; i++, j++){ if( fossil_isspace(zBlob[i]) || zBlob[i]==',' ){ while( fossil_isspace(zBlob[i+1]) ){ i++; } |
︙ | ︙ | |||
339 340 341 342 343 344 345 | /* Extract the tags in sorted order and make an entry in the ** artifact for each. */ db_prepare(&q, "SELECT x FROM newtags ORDER BY x"); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&event, "T +sym-%F *\n", db_column_text(&q, 0)); } db_finalize(&q); | | | | | > > > > > > > | | | > | > | > | | < < < < | < | | | | | | | | > > > > > | | 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | /* Extract the tags in sorted order and make an entry in the ** artifact for each. */ db_prepare(&q, "SELECT x FROM newtags ORDER BY x"); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(&event, "T +sym-%F *\n", db_column_text(&q, 0)); } db_finalize(&q); } if( !login_is_nobody() ){ blob_appendf(&event, "U %F\n", login_name()); } blob_appendf(&event, "W %d\n%s\n", strlen(zBody), zBody); md5sum_blob(&event, &cksum); blob_appendf(&event, "Z %b\n", &cksum); blob_reset(&cksum); nrid = content_put(&event); db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); if( manifest_crosslink(nrid, &event, MC_NONE)==0 ){ db_end_transaction(1); style_header("Error"); @ Internal error: Fossil tried to make an invalid artifact for @ the edited technode. style_footer(); return; } assert( blob_is_reset(&event) ); content_deltify(rid, nrid, 0); db_end_transaction(0); cgi_redirectf("technote?name=%T", zId); } if( P("cancel")!=0 ){ cgi_redirectf("technote?name=%T", zId); return; } if( zBody==0 ){ zBody = mprintf("Insert new content here..."); } if( isNew ){ style_header("New Tech-note %S", zId); }else{ style_header("Edit Tech-note %S", zId); } if( P("preview")!=0 ){ Blob com; @ <p><b>Timeline comment preview:</b></p> @ <blockquote> @ <table border="0"> if( zClr && zClr[0] ){ @ <tr><td style="background-color: %h(zClr);"> }else{ @ <tr><td> } blob_zero(&com); blob_append(&com, zComment, -1); wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS); @ </td></tr></table> @ </blockquote> @ <p><b>Page content preview:</b><p> @ <blockquote> blob_init(&event, 0, 0); blob_append(&event, zBody, -1); wiki_render_by_mimetype(&event, zMimetype); @ </blockquote><hr /> blob_reset(&event); } for(n=2, z=zBody; z[0]; z++){ if( z[0]=='\n' ) n++; } if( n<20 ) n = 20; if( n>40 ) n = 40; @ <form method="post" action="%R/technoteedit"><div> login_insert_csrf_secret(); @ <input type="hidden" name="name" value="%h(zId)" /> @ <table border="0" cellspacing="10"> @ <tr><th align="right" valign="top">Timestamp (UTC):</th> @ <td valign="top"> @ <input type="text" name="t" size="25" value="%h(zETime)" /> @ </td></tr> @ <tr><th align="right" valign="top">Timeline Comment:</th> @ <td valign="top"> @ <textarea name="c" class="technoteedit" cols="80" @ rows="3" wrap="virtual">%h(zComment)</textarea> @ </td></tr> @ <tr><th align="right" valign="top">Timeline Background Color:</th> @ <td valign="top"> render_color_chooser(0, zClr, 0, "clr", "cclr"); @ </td></tr> @ <tr><th align="right" valign="top">Tags:</th> @ <td valign="top"> @ <input type="text" name="g" size="40" value="%h(zTags)" /> @ </td></tr> @ <tr><th align="right" valign="top">Markup Style:</th> @ <td valign="top"> mimetype_option_menu(zMimetype); @ </td></tr> @ <tr><th align="right" valign="top">Page Content:</th> @ <td valign="top"> @ <textarea name="w" class="technoteedit" cols="80" @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> @ </td></tr> @ <tr><td colspan="2"> @ <input type="submit" name="preview" value="Preview Your Changes" /> @ <input type="submit" name="submit" value="Apply These Changes" /> @ <input type="submit" name="cancel" value="Cancel" /> @ </td></tr></table> @ </div></form> style_footer(); } |
Changes to src/export.c.
︙ | ︙ | |||
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | } } zName[j] = 0; printf(" %s <%s>", zName, zUser); free(zName); return; } zContact = db_column_text(&q, 0); for(i=0; zContact[i] && zContact[i]!='>' && zContact[i]!='<'; i++){} if( zContact[i]==0 ){ printf(" %s <%s>", zContact[0] ? zContact : zUser, zUser); db_reset(&q); return; } if( zContact[i]=='<' ){ zEmail = mprintf("%s", &zContact[i]); | > > > > > > > > > | | > > > > > > > | | | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | } } zName[j] = 0; printf(" %s <%s>", zName, zUser); free(zName); return; } /* ** We have contact information. ** It may or may not contain an email address. */ zContact = db_column_text(&q, 0); for(i=0; zContact[i] && zContact[i]!='>' && zContact[i]!='<'; i++){} if( zContact[i]==0 ){ /* No email address found. Take as user info if not empty */ printf(" %s <%s>", zContact[0] ? zContact : zUser, zUser); db_reset(&q); return; } if( zContact[i]=='<' ){ /* ** Found beginning of email address. Look for the end and extract ** the part. */ zEmail = mprintf("%s", &zContact[i]); for(j=0; zEmail[j] && zEmail[j]!='>'; j++){} if( zEmail[j]=='>' ) zEmail[j+1] = 0; }else{ /* ** Found an end marker for email, but nothing else. */ zEmail = mprintf("<%s>", zUser); } /* ** Here zContact[i] either '<' or '>'. Extract the string _before_ ** either as user name. */ zName = mprintf("%.*s", i-1, zContact); for(i=j=0; zName[i]; i++){ if( zName[i]!='"' ) zName[j++] = zName[i]; } zName[j] = 0; printf(" %s %s", zName, zEmail); free(zName); free(zEmail); db_reset(&q); } #define BLOBMARK(rid) ((rid) * 2) #define COMMITMARK(rid) ((rid) * 2 + 1) /* ** COMMAND: export ** ** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY? ** ** Write an export of all check-ins to standard output. The export is ** written in the git-fast-export file format assuming the --git option is ** provided. The git-fast-export format is currently the only VCS ** interchange format supported, though other formats may be added in ** the future. ** ** Run this command within a checkout. Or use the -R or --repository ** option to specify a Fossil repository to be exported. ** ** Only check-ins are exported using --git. Git does not support tickets ** or wiki or events or attachments, so none of those are exported. ** ** If the "--import-marks FILE" option is used, it contains a list of ** rids to skip. ** ** If the "--export-marks FILE" option is used, the rid of all commits and ** blobs written on exit for use with "--import-marks" on the next run. ** ** Options: ** --export-marks FILE export rids of exported data to FILE ** --import-marks FILE read rids of data to ignore from FILE ** --repository|-R REPOSITORY export the given REPOSITORY ** ** See also: import */ void export_cmd(void){ Stmt q, q2, q3; int i; Bag blobs, vers; const char *markfile_in; |
︙ | ︙ | |||
161 162 163 164 165 166 167 | } db_finalize(&qb); db_finalize(&qc); fclose(f); } /* Step 1: Generate "blob" records for every artifact that is part | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | } db_finalize(&qb); db_finalize(&qc); fclose(f); } /* Step 1: Generate "blob" records for every artifact that is part ** of a check-in */ fossil_binary_mode(stdout); db_multi_exec("CREATE TEMP TABLE newblob(rid INTEGER KEY, srcid INTEGER)"); db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)"); db_multi_exec( "INSERT INTO newblob" " SELECT DISTINCT fid," |
︙ | ︙ | |||
214 215 216 217 218 219 220 | db_finalize(&q); db_finalize(&q2); db_finalize(&q3); /* Output the commit records. */ db_prepare(&q, | | | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | db_finalize(&q); db_finalize(&q2); db_finalize(&q3); /* Output the commit records. */ db_prepare(&q, "SELECT strftime('%%s',mtime), objid, coalesce(ecomment,comment)," " coalesce(euser,user)," " (SELECT value FROM tagxref WHERE rid=objid AND tagid=%d)" " FROM event" " WHERE type='ci' AND NOT EXISTS (SELECT 1 FROM oldcommit WHERE objid=rid)" " ORDER BY mtime ASC", TAG_BRANCH ); db_prepare(&q2, "INSERT INTO oldcommit VALUES (:rid)"); |
︙ | ︙ |
Changes to src/file.c.
︙ | ︙ | |||
37 38 39 40 41 42 43 | # include <direct.h> # include <windows.h> # include <sys/utime.h> #else # include <sys/time.h> #endif | > | < < > | > > > > > | | > > > > > > | > > > | > | | > < < | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | # include <direct.h> # include <windows.h> # include <sys/utime.h> #else # include <sys/time.h> #endif #if INTERFACE #include <dirent.h> #if defined(_WIN32) # define DIR _WDIR # define dirent _wdirent # define opendir _wopendir # define readdir _wreaddir # define closedir _wclosedir #endif /* _WIN32 */ #if defined(_WIN32) && (defined(__MSVCRT__) || defined(_MSC_VER)) struct fossilStat { i64 st_size; i64 st_mtime; int st_mode; }; #endif #endif /* INTERFACE */ #if !defined(_WIN32) || !(defined(__MSVCRT__) || defined(_MSC_VER)) # define fossilStat stat #endif /* ** On Windows S_ISLNK always returns FALSE. */ #if !defined(S_ISLNK) # define S_ISLNK(x) (0) #endif static int fileStatValid = 0; static struct fossilStat fileStat; /* ** Fill stat buf with information received from stat() or lstat(). ** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on. ** */ static int fossil_stat(const char *zFilename, struct fossilStat *buf, int isWd){ int rc; void *zMbcs = fossil_utf8_to_filename(zFilename); #if !defined(_WIN32) if( isWd && g.allowSymlinks ){ rc = lstat(zMbcs, buf); }else{ rc = stat(zMbcs, buf); } #else rc = win32_stat(zMbcs, buf, isWd); #endif fossil_filename_free(zMbcs); return rc; } /* ** Fill in the fileStat variable for the file named zFilename. |
︙ | ︙ | |||
216 217 218 219 220 221 222 | blob_read_link(&content, zFrom); symlink_create(blob_str(&content), zTo); blob_reset(&content); } /* ** Return file permissions (normal, executable, or symlink): | | < | < < | | | < < < < < < | | > | | < | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | blob_read_link(&content, zFrom); symlink_create(blob_str(&content), zTo); blob_reset(&content); } /* ** Return file permissions (normal, executable, or symlink): ** - PERM_EXE on Unix if file is executable; ** - PERM_LNK on Unix if file is symlink and allow-symlinks option is on; ** - PERM_REG for all other cases (regular file, directory, fifo, etc). */ int file_wd_perm(const char *zFilename){ #if !defined(_WIN32) if( !getStat(zFilename, 1) ){ if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 ) return PERM_EXE; else if( g.allowSymlinks && S_ISLNK(fileStat.st_mode) ) return PERM_LNK; } #endif return PERM_REG; } /* ** Return TRUE if the named file is an executable. Return false ** for directories, devices, fifos, symlinks, etc. */ int file_wd_isexe(const char *zFilename){ |
︙ | ︙ | |||
300 301 302 303 304 305 306 307 | } /* ** Wrapper around the access() system call. */ int file_access(const char *zFilename, int flags){ #ifdef _WIN32 | > > < | < | > > < | < | | 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | } /* ** Wrapper around the access() system call. */ int file_access(const char *zFilename, int flags){ int rc; void *zMbcs = fossil_utf8_to_filename(zFilename); #ifdef _WIN32 rc = win32_access(zMbcs, flags); #else rc = access(zMbcs, flags); #endif fossil_filename_free(zMbcs); return rc; } /* ** Wrapper around the chdir() system call. ** If bChroot=1, do a chroot to this dir as well ** (UNIX only) */ int file_chdir(const char *zChDir, int bChroot){ int rc; void *zPath = fossil_utf8_to_filename(zChDir); #ifdef _WIN32 rc = win32_chdir(zPath, bChroot); #else rc = chdir(zPath); if( !rc && bChroot ){ rc = chroot(zPath); if( !rc ) rc = chdir("/"); } #endif fossil_filename_free(zPath); return rc; |
︙ | ︙ | |||
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | */ void file_copy(const char *zFrom, const char *zTo){ FILE *in, *out; int got; char zBuf[8192]; in = fossil_fopen(zFrom, "rb"); if( in==0 ) fossil_fatal("cannot open \"%s\" for reading", zFrom); out = fossil_fopen(zTo, "wb"); if( out==0 ) fossil_fatal("cannot open \"%s\" for writing", zTo); while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ){ fwrite(zBuf, 1, got, out); } fclose(in); fclose(out); } /* ** Set or clear the execute bit on a file. Return true if a change ** occurred and false if this routine is a no-op. */ int file_wd_setexe(const char *zFilename, int onoff){ int rc = 0; #if !defined(_WIN32) struct stat buf; if( fossil_stat(zFilename, &buf, 1)!=0 || S_ISLNK(buf.st_mode) ) return 0; if( onoff ){ int targetMode = (buf.st_mode & 0444)>>2; | > > > > > > > > > > > > > > > > > | | > | | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | */ void file_copy(const char *zFrom, const char *zTo){ FILE *in, *out; int got; char zBuf[8192]; in = fossil_fopen(zFrom, "rb"); if( in==0 ) fossil_fatal("cannot open \"%s\" for reading", zFrom); file_mkfolder(zTo, 0); out = fossil_fopen(zTo, "wb"); if( out==0 ) fossil_fatal("cannot open \"%s\" for writing", zTo); while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ){ fwrite(zBuf, 1, got, out); } fclose(in); fclose(out); } /* ** COMMAND: test-file-copy ** ** Usage: %fossil test-file-copy SOURCE DESTINATION ** ** Make a copy of the file at SOURCE into a new name DESTINATION. Any ** directories in the path leading up to DESTINATION that do not already ** exist are created automatically. */ void test_file_copy(void){ if( g.argc!=4 ){ fossil_fatal("Usage: %s test-file-copy SOURCE DESTINATION", g.argv[0]); } file_copy(g.argv[2], g.argv[3]); } /* ** Set or clear the execute bit on a file. Return true if a change ** occurred and false if this routine is a no-op. */ int file_wd_setexe(const char *zFilename, int onoff){ int rc = 0; #if !defined(_WIN32) struct stat buf; if( fossil_stat(zFilename, &buf, 1)!=0 || S_ISLNK(buf.st_mode) ) return 0; if( onoff ){ int targetMode = (buf.st_mode & 0444)>>2; if( (buf.st_mode & 0100) == 0 ){ chmod(zFilename, buf.st_mode | targetMode); rc = 1; } }else{ if( (buf.st_mode & 0100) != 0 ){ chmod(zFilename, buf.st_mode & ~0111); rc = 1; } } #endif /* _WIN32 */ return rc; } /* ** Set the mtime for a file. */ void file_set_mtime(const char *zFilename, i64 newMTime){ #if !defined(_WIN32) char *zMbcs; struct timeval tv[2]; memset(tv, 0, sizeof(tv[0])*2); tv[0].tv_sec = newMTime; tv[1].tv_sec = newMTime; zMbcs = fossil_utf8_to_filename(zFilename); utimes(zMbcs, tv); #else struct _utimbuf tb; wchar_t *zMbcs = fossil_utf8_to_filename(zFilename); tb.actime = newMTime; tb.modtime = newMTime; _wutime(zMbcs, &tb); |
︙ | ︙ | |||
501 502 503 504 505 506 507 508 509 510 511 512 513 514 | rc = mkdir(zName, 0755); #endif fossil_filename_free(zMbcs); return rc; } return 0; } /* ** Removes the directory named in the argument, if it exists. The directory ** must be empty and cannot be the current directory or the root directory. ** ** Returns zero upon success. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | rc = mkdir(zName, 0755); #endif fossil_filename_free(zMbcs); return rc; } return 0; } /* ** Create the tree of directories in which zFilename belongs, if that sequence ** of directories does not already exist. */ void file_mkfolder(const char *zFilename, int forceFlag){ int i, nName; char *zName; nName = strlen(zFilename); zName = mprintf("%s", zFilename); nName = file_simplify_name(zName, nName, 0); for(i=1; i<nName; i++){ if( zName[i]=='/' ){ zName[i] = 0; #if defined(_WIN32) || defined(__CYGWIN__) /* ** On Windows, local path looks like: C:/develop/project/file.txt ** The if stops us from trying to create a directory of a drive letter ** C: in this example. */ if( !(i==2 && zName[1]==':') ){ #endif if( file_mkdir(zName, forceFlag) && file_isdir(zName)!=1 ){ fossil_fatal_recursive("unable to create directory %s", zName); return; } #if defined(_WIN32) || defined(__CYGWIN__) } #endif zName[i] = '/'; } } free(zName); } /* ** Removes the directory named in the argument, if it exists. The directory ** must be empty and cannot be the current directory or the root directory. ** ** Returns zero upon success. */ |
︙ | ︙ | |||
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 | *pJ = i-1; return 1; } /* ** Simplify a filename by ** ** * Convert all \ into / on windows and cygwin ** * removing any trailing and duplicate / ** * removing /./ ** * removing /A/../ ** ** Changes are made in-place. Return the new name length. ** If the slash parameter is non-zero, the trailing slash, if any, ** is retained. */ int file_simplify_name(char *z, int n, int slash){ | > | | > | | > > > > > > > > > | | 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 | *pJ = i-1; return 1; } /* ** Simplify a filename by ** ** * Remove extended path prefix on windows and cygwin ** * Convert all \ into / on windows and cygwin ** * removing any trailing and duplicate / ** * removing /./ ** * removing /A/../ ** ** Changes are made in-place. Return the new name length. ** If the slash parameter is non-zero, the trailing slash, if any, ** is retained. */ int file_simplify_name(char *z, int n, int slash){ int i = 1, j; if( n<0 ) n = strlen(z); /* On windows and cygwin convert all \ characters to / * and remove extended path prefix if present */ #if defined(_WIN32) || defined(__CYGWIN__) for(j=0; j<n; j++){ if( z[j]=='\\' ) z[j] = '/'; } if( n>3 && !memcmp(z, "//?/", 4) ){ if( fossil_strnicmp(z+4,"UNC", 3) ){ i += 4; z[0] = z[4]; }else{ i += 6; z[0] = '/'; } } #endif /* Removing trailing "/" characters */ if( !slash ){ while( n>1 && z[n-1]=='/' ){ n--; } } /* Remove duplicate '/' characters. Except, two // at the beginning ** of a pathname is allowed since this is important on windows. */ for(j=1; i<n; i++){ z[j++] = z[i]; while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++; } n = j; /* Skip over zero or more initial "./" sequences */ for(i=0; i<n-1 && z[i]=='.' && z[i+1]=='/'; i+=2){} |
︙ | ︙ | |||
688 689 690 691 692 693 694 | i += 2; continue; } } if( j>=0 ) z[j] = z[i]; j++; } | | | 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 | i += 2; continue; } } if( j>=0 ) z[j] = z[i]; j++; } if( j==0 ) z[j++] = '/'; z[j] = 0; return j; } /* ** COMMAND: test-simplify-name ** |
︙ | ︙ | |||
721 722 723 724 725 726 727 | ** ** On windows, the name is converted from unicode to UTF8 and all '\\' ** characters are converted to '/'. No conversions are needed on ** unix. */ void file_getcwd(char *zBuf, int nBuf){ #ifdef _WIN32 | < < < < < < < < < < < < < | < | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 | ** ** On windows, the name is converted from unicode to UTF8 and all '\\' ** characters are converted to '/'. No conversions are needed on ** unix. */ void file_getcwd(char *zBuf, int nBuf){ #ifdef _WIN32 win32_getcwd(zBuf, nBuf); #else if( getcwd(zBuf, nBuf-1)==0 ){ if( errno==ERANGE ){ fossil_fatal("pwd too big: max %d\n", nBuf-1); }else{ fossil_fatal("cannot find current working directory; %s", strerror(errno)); |
︙ | ︙ | |||
757 758 759 760 761 762 763 | ** if it is relative. */ int file_is_absolute_path(const char *zPath){ if( zPath[0]=='/' #if defined(_WIN32) || defined(__CYGWIN__) || zPath[0]=='\\' || (fossil_isalpha(zPath[0]) && zPath[1]==':' | | > > > > > > > > > > > > > > > > > > < < < < | < < < < < < < < < < | < < < | 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 | ** if it is relative. */ int file_is_absolute_path(const char *zPath){ if( zPath[0]=='/' #if defined(_WIN32) || defined(__CYGWIN__) || zPath[0]=='\\' || (fossil_isalpha(zPath[0]) && zPath[1]==':' && (zPath[2]=='\\' || zPath[2]=='/' || zPath[2]=='\0')) #endif ){ return 1; }else{ return 0; } } /* ** Compute a canonical pathname for a file or directory. ** Make the name absolute if it is relative. ** Remove redundant / characters ** Remove all /./ path elements. ** Convert /A/../ to just / ** If the slash parameter is non-zero, the trailing slash, if any, ** is retained. */ void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ blob_zero(pOut); if( file_is_absolute_path(zOrigName) ){ blob_appendf(pOut, "%/", zOrigName); }else{ char zPwd[2000]; file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); if( zPwd[0]=='/' && strlen(zPwd)==1 ){ /* when on '/', don't add an extra '/' */ if( zOrigName[0]=='.' && strlen(zOrigName)==1 ){ /* '.' when on '/' mean '/' */ blob_appendf(pOut, "%/", zPwd); }else{ blob_appendf(pOut, "%/%/", zPwd, zOrigName); } }else{ blob_appendf(pOut, "%//%/", zPwd, zOrigName); } } #if defined(_WIN32) || defined(__CYGWIN__) { char *zOut; /* ** On Windows/cygwin, normalize the drive letter to upper case. */ zOut = blob_str(pOut); if( fossil_islower(zOut[0]) && zOut[1]==':' && zOut[2]=='/' ){ zOut[0] = fossil_toupper(zOut[0]); } } #endif blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut), slash)); } /* ** COMMAND: test-canonical-name ** Usage: %fossil test-canonical-name FILENAME... |
︙ | ︙ | |||
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 | }else{ blob_set(pOut, ".."); for(j=i+1; zPwd[j]; j++){ if( zPwd[j]=='/' ){ blob_append(pOut, "/..", 3); } } } if( slash && i>0 && zPath[strlen(zPath)-1]=='/'){ blob_append(pOut, "/", 1); } blob_reset(&tmp); return; } if( zPwd[i]==0 && zPath[i]=='/' ){ memcpy(&tmp, pOut, sizeof(tmp)); blob_set(pOut, "./"); blob_append(pOut, &zPath[i+1], -1); blob_reset(&tmp); return; } while( zPath[i-1]!='/' ){ i--; } | > > > > > > | > | 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 | }else{ blob_set(pOut, ".."); for(j=i+1; zPwd[j]; j++){ if( zPwd[j]=='/' ){ blob_append(pOut, "/..", 3); } } while( i>0 && (zPwd[i]!='/')) --i; blob_append(pOut, zPath+i, j-i); } if( slash && i>0 && zPath[strlen(zPath)-1]=='/'){ blob_append(pOut, "/", 1); } blob_reset(&tmp); return; } if( zPwd[i]==0 && zPath[i]=='/' ){ memcpy(&tmp, pOut, sizeof(tmp)); blob_set(pOut, "./"); blob_append(pOut, &zPath[i+1], -1); blob_reset(&tmp); return; } while( zPath[i-1]!='/' ){ i--; } if( zPwd[0]=='/' && strlen(zPwd)==1 ){ /* If on '/', don't go to higher level */ blob_zero(&tmp); }else{ blob_set(&tmp, "../"); } for(j=i; zPwd[j]; j++){ if( zPwd[j]=='/' ){ blob_append(&tmp, "../", 3); } } blob_append(&tmp, &zPath[i], -1); blob_reset(pOut); |
︙ | ︙ | |||
974 975 976 977 978 979 980 | char *zLocalRoot; Blob full; int nFull; char *zFull; int (*xCmp)(const char*,const char*,int); blob_zero(pOut); | > > | > | > | 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 | char *zLocalRoot; Blob full; int nFull; char *zFull; int (*xCmp)(const char*,const char*,int); blob_zero(pOut); if( !g.localOpen ){ blob_appendf(pOut, "%s", zOrigName); return 1; } file_canonical_name(g.zLocalRoot, &localRoot, 1); nLocalRoot = blob_size(&localRoot); zLocalRoot = blob_buffer(&localRoot); assert( nLocalRoot>0 && zLocalRoot[nLocalRoot-1]=='/' ); file_canonical_name(zOrigName, &full, 0); nFull = blob_size(&full); zFull = blob_buffer(&full); if( filenames_are_case_sensitive() ){ xCmp = fossil_strncmp; }else{ xCmp = fossil_strnicmp; } /* Special case. zOrigName refers to g.zLocalRoot directory. */ if( (nFull==nLocalRoot-1 && xCmp(zLocalRoot, zFull, nFull)==0) || (nFull==1 && zFull[0]=='/' && nLocalRoot==1 && zLocalRoot[0]=='/') ){ blob_append(pOut, ".", 1); blob_reset(&localRoot); blob_reset(&full); return 1; } if( nFull<=nLocalRoot || xCmp(zLocalRoot, zFull, nLocalRoot) ){ |
︙ | ︙ | |||
1022 1023 1024 1025 1026 1027 1028 1029 | ** Options: ** --case-sensitive B Enable or disable case-sensitive filenames. B is ** a boolean: "yes", "no", "true", "false", etc. */ void cmd_test_tree_name(void){ int i; Blob x; blob_zero(&x); | > < | 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 | ** Options: ** --case-sensitive B Enable or disable case-sensitive filenames. B is ** a boolean: "yes", "no", "true", "false", etc. */ void cmd_test_tree_name(void){ int i; Blob x; db_find_and_open_repository(0,0); blob_zero(&x); for(i=2; i<g.argc; i++){ if( file_tree_name(g.argv[i], &x, 1) ){ fossil_print("%s\n", blob_buffer(&x)); blob_reset(&x); } } } |
︙ | ︙ | |||
1169 1170 1171 1172 1173 1174 1175 | blob_read_from_file(&onDisk, zName); } rc = blob_compare(&onDisk, pContent); blob_reset(&onDisk); return rc==0; } | < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > | 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 | blob_read_from_file(&onDisk, zName); } rc = blob_compare(&onDisk, pContent); blob_reset(&onDisk); return rc==0; } /* ** Return the value of an environment variable as UTF8. ** Use fossil_filename_free() to release resources. */ char *fossil_getenv(const char *zName){ #ifdef _WIN32 wchar_t *uName = fossil_utf8_to_unicode(zName); void *zValue = _wgetenv(uName); fossil_unicode_free(uName); #else char *zValue = getenv(zName); #endif if( zValue ) zValue = fossil_filename_to_utf8(zValue); return zValue; } /* ** Sets the value of an environment variable as UTF8. */ int fossil_setenv(const char *zName, const char *zValue){ int rc; char *zString = mprintf("%s=%s", zName, zValue); #ifdef _WIN32 wchar_t *uString = fossil_utf8_to_unicode(zString); rc = _wputenv(uString); fossil_unicode_free(uString); fossil_free(zString); #else rc = putenv(zString); /* NOTE: Cannot free the string on POSIX. */ /* fossil_free(zString); */ #endif return rc; } /* ** Like fopen() but always takes a UTF8 argument. */ FILE *fossil_fopen(const char *zName, const char *zMode){ #ifdef _WIN32 wchar_t *uMode = fossil_utf8_to_unicode(zMode); |
︙ | ︙ |
Changes to src/finfo.c.
︙ | ︙ | |||
41 42 43 44 45 46 47 | ** to stdout. The -p mode is another form of the "cat" command. ** ** Options: ** -b|--brief display a brief (one line / revision) summary ** --case-sensitive B Enable or disable case-sensitive filenames. B is a ** boolean: "yes", "no", "true", "false", etc. ** -l|--log select log mode (the default) | | > > > > < > > > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | ** to stdout. The -p mode is another form of the "cat" command. ** ** Options: ** -b|--brief display a brief (one line / revision) summary ** --case-sensitive B Enable or disable case-sensitive filenames. B is a ** boolean: "yes", "no", "true", "false", etc. ** -l|--log select log mode (the default) ** -n|--limit N Display the first N changes (default unlimited). ** N<=0 means no limit. ** --offset P skip P changes ** -p|--print select print mode ** -r|--revision R print the given revision (or ckout, if none is given) ** to stdout (only in print mode) ** -s|--status select status mode (print a status indicator for FILE) ** -W|--width <num> Width of lines (default is to auto-detect). Must be ** >22 or 0 (= no limit, resulting in a single line per ** entry). ** ** See also: artifact, cat, descendants, info, leaves */ void finfo_cmd(void){ db_must_be_within_tree(); if( find_option("status","s",0) ){ Stmt q; Blob line; Blob fname; int vid; /* We should be done with options.. */ verify_all_options(); if( g.argc!=3 ) usage("-s|--status FILENAME"); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("no checkout to finfo files in"); } vfile_check_signature(vid, CKSIG_ENOTFILE); |
︙ | ︙ | |||
110 111 112 113 114 115 116 117 118 119 120 121 122 123 | fossil_print("%s\n", blob_str(&line)); blob_reset(&fname); blob_reset(&line); }else if( find_option("print","p",0) ){ Blob record; Blob fname; const char *zRevision = find_option("revision", "r", 1); file_tree_name(g.argv[2], &fname, 1); if( zRevision ){ historical_version_of_file(zRevision, blob_str(&fname), &record, 0,0,0,0); }else{ int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s", &fname, filename_collation()); | > > > | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | fossil_print("%s\n", blob_str(&line)); blob_reset(&fname); blob_reset(&line); }else if( find_option("print","p",0) ){ Blob record; Blob fname; const char *zRevision = find_option("revision", "r", 1); /* We should be done with options.. */ verify_all_options(); file_tree_name(g.argv[2], &fname, 1); if( zRevision ){ historical_version_of_file(zRevision, blob_str(&fname), &record, 0,0,0,0); }else{ int rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s", &fname, filename_collation()); |
︙ | ︙ | |||
132 133 134 135 136 137 138 139 | }else{ Blob line; Stmt q; Blob fname; int rid; const char *zFilename; const char *zLimit; const char *zOffset; | > | > > > > > > > > > > > > > > > > | | > | | | | | | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | }else{ Blob line; Stmt q; Blob fname; int rid; const char *zFilename; const char *zLimit; const char *zWidth; const char *zOffset; int iLimit, iOffset, iBrief, iWidth; if( find_option("log","l",0) ){ /* this is the default, no-op */ } zLimit = find_option("limit","n",1); zWidth = find_option("width","W",1); iLimit = zLimit ? atoi(zLimit) : -1; zOffset = find_option("offset",0,1); iOffset = zOffset ? atoi(zOffset) : 0; iBrief = (find_option("brief","b",0) == 0); if( iLimit==0 ){ iLimit = -1; } if( zWidth ){ iWidth = atoi(zWidth); if( (iWidth!=0) && (iWidth<=22) ){ fossil_fatal("-W|--width value must be >22 or 0"); } }else{ iWidth = -1; } /* We should be done with options.. */ verify_all_options(); if( g.argc!=3 ){ usage("?-l|--log? ?-b|--brief? FILENAME"); } file_tree_name(g.argv[2], &fname, 1); rid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%B %s", &fname, filename_collation()); if( rid==0 ){ fossil_fatal("no history for file: %b", &fname); } zFilename = blob_str(&fname); db_prepare(&q, "SELECT b.uuid, ci.uuid, date(event.mtime%s)," " coalesce(event.ecomment, event.comment)," " coalesce(event.euser, event.user)," " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" " AND tagxref.rid=mlink.mid)" /* Tags */ " FROM mlink, blob b, event, blob ci, filename" " WHERE filename.name=%Q %s" " AND mlink.fnid=filename.fnid" " AND b.rid=mlink.fid" " AND event.objid=mlink.mid" " AND event.objid=ci.rid" " ORDER BY event.mtime DESC LIMIT %d OFFSET %d", timeline_utc(), TAG_BRANCH, zFilename, filename_collation(), iLimit, iOffset ); blob_zero(&line); if( iBrief ){ fossil_print("History of %s\n", blob_str(&fname)); } while( db_step(&q)==SQLITE_ROW ){ const char *zFileUuid = db_column_text(&q, 0); const char *zCiUuid = db_column_text(&q,1); const char *zDate = db_column_text(&q, 2); const char *zCom = db_column_text(&q, 3); const char *zUser = db_column_text(&q, 4); const char *zBr = db_column_text(&q, 5); char *zOut; if( zBr==0 ) zBr = "trunk"; if( iBrief ){ fossil_print("%s ", zDate); zOut = mprintf( "[%S] %s (user: %s, artifact: [%S], branch: %s)", zCiUuid, zCom, zUser, zFileUuid, zBr); comment_print(zOut, zCom, 11, iWidth, g.comFmtFlags); fossil_free(zOut); }else{ blob_reset(&line); blob_appendf(&line, "%S ", zCiUuid); blob_appendf(&line, "%.10s ", zDate); blob_appendf(&line, "%8.8s ", zUser); blob_appendf(&line, "%8.8s ", zBr); blob_appendf(&line,"%-39.39s", zCom ); comment_print(blob_str(&line), zCom, 0, iWidth, g.comFmtFlags); } } db_finalize(&q); blob_reset(&fname); } } |
︙ | ︙ | |||
225 226 227 228 229 230 231 232 233 234 235 236 237 238 | void cat_cmd(void){ int i; int rc; Blob content, fname; const char *zRev; db_find_and_open_repository(0, 0); zRev = find_option("r","r",1); for(i=2; i<g.argc; i++){ file_tree_name(g.argv[i], &fname, 1); blob_zero(&content); rc = historical_version_of_file(zRev, blob_str(&fname), &content, 0,0,0,0); if( rc==0 ){ fossil_fatal("no such file: %s", g.argv[i]); } | > > > > | 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | void cat_cmd(void){ int i; int rc; Blob content, fname; const char *zRev; db_find_and_open_repository(0, 0); zRev = find_option("r","r",1); /* We should be done with options.. */ verify_all_options(); for(i=2; i<g.argc; i++){ file_tree_name(g.argv[i], &fname, 1); blob_zero(&content); rc = historical_version_of_file(zRev, blob_str(&fname), &content, 0,0,0,0); if( rc==0 ){ fossil_fatal("no such file: %s", g.argv[i]); } |
︙ | ︙ | |||
255 256 257 258 259 260 261 | ** ** a=DATE Only show changes after DATE ** b=DATE Only show changes before DATE ** n=NUM Show the first NUM changes only ** brbg Background color by branch name ** ubg Background color by user name ** ci=UUID Ancestors of a particular check-in | < | > < > | < < < > > > > > > > > > > > > > > | | | | < < < < < < < < < < < < < < < | | < < < < | | < > > > > > > > > | < > > | > | < < < < < < < < < < | | | > | > | > | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | ** ** a=DATE Only show changes after DATE ** b=DATE Only show changes before DATE ** n=NUM Show the first NUM changes only ** brbg Background color by branch name ** ubg Background color by user name ** ci=UUID Ancestors of a particular check-in */ void finfo_page(void){ Stmt q; const char *zFilename; char zPrevDate[20]; const char *zA; const char *zB; int n; int baseCheckin; int fnid; Bag ancestor; Blob title; Blob sql; HQuery url; GraphContext *pGraph; int brBg = P("brbg")!=0; int uBg = P("ubg")!=0; int fDebug = atoi(PD("debug","0")); int fShowId = P("showid")!=0; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("File History"); login_anonymous_available(); url_initialize(&url, "finfo"); if( brBg ) url_add_parameter(&url, "brbg", 0); if( uBg ) url_add_parameter(&url, "ubg", 0); baseCheckin = name_to_rid_www("ci"); zPrevDate[0] = 0; zFilename = PD("name",""); fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); if( fnid==0 ){ @ No such file: %h(zFilename) style_footer(); return; } if( baseCheckin ){ int baseFid = db_int(0, "SELECT fid FROM mlink WHERE fnid=%d AND mid=%d", fnid, baseCheckin ); bag_init(&ancestor); if( baseFid ) bag_insert(&ancestor, baseFid); } url_add_parameter(&url, "name", zFilename); blob_zero(&sql); blob_append_sql(&sql, "SELECT" " datetime(min(event.mtime)%s)," /* Date of change */ " coalesce(event.ecomment, event.comment)," /* Check-in comment */ " coalesce(event.euser, event.user)," /* User who made chng */ " mlink.pid," /* Parent file rid */ " mlink.fid," /* File rid */ " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */ " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* Current file uuid */ " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* Check-in uuid */ " event.bgcolor," /* Background color */ " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" " AND tagxref.rid=mlink.mid)," /* Branchname */ " mlink.mid," /* check-in ID */ " mlink.pfnid" /* Previous filename */ " FROM mlink, event" " WHERE mlink.fnid=%d" " AND event.objid=mlink.mid", timeline_utc(), TAG_BRANCH, fnid ); if( (zA = P("a"))!=0 ){ blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zA); url_add_parameter(&url, "a", zA); } if( (zB = P("b"))!=0 ){ blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zB); url_add_parameter(&url, "b", zB); } /* We only want each version of a file to appear on the graph once, ** at its earliest appearance. All the other times that it gets merged ** into this or that branch can be ignored. An exception is for when ** files are deleted (when they have mlink.fid==0). If the same file ** is deleted in multiple places, we want to show each deletion, so ** use a "fake fid" which is derived from the parent-fid for grouping. ** The same fake-fid must be used on the graph. */ blob_append_sql(&sql, " GROUP BY" " CASE WHEN mlink.fid>0 THEN mlink.fid ELSE mlink.pid+1000000000 END" " ORDER BY event.mtime DESC /*sort*/" ); if( (n = atoi(PD("n","0")))>0 ){ blob_append_sql(&sql, " LIMIT %d", n); url_add_parameter(&url, "n", P("n")); } db_prepare(&q, "%s", blob_sql_text(&sql)); if( P("showsql")!=0 ){ @ <p>SQL: %h(blob_str(&sql))</p> } blob_reset(&sql); blob_zero(&title); if( baseCheckin ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin); char *zLink = href("%R/info/%!S", zUuid); blob_appendf(&title, "Ancestors of file "); hyperlinked_path(zFilename, &title, zUuid, "tree", ""); if( fShowId ) blob_appendf(&title, " (%d)", fnid); blob_appendf(&title, " from check-in %z%S</a>", zLink, zUuid); if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin); fossil_free(zUuid); }else{ blob_appendf(&title, "History of files named "); hyperlinked_path(zFilename, &title, 0, "tree", ""); if( fShowId ) blob_appendf(&title, " (%d)", fnid); } @ <h2>%b(&title)</h2> blob_reset(&title); pGraph = graph_init(); @ <div id="canvas" style="position:relative;width:1px;height:1px;" @ onclick="clickOnGraph(event)"></div> @ <table id="timelineTable" class="timelineTable"> |
︙ | ︙ | |||
393 394 395 396 397 398 399 | const char *zCkin = db_column_text(&q,7); const char *zBgClr = db_column_text(&q, 8); const char *zBr = db_column_text(&q, 9); int fmid = db_column_int(&q, 10); int pfnid = db_column_int(&q, 11); int gidx; char zTime[10]; | > > > | > > > > | > > > > > > > > > > | > | | < < | | > > > > | > > > | | > > > > > | < < < | < > | | | > > | | > > > > | | | 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | const char *zCkin = db_column_text(&q,7); const char *zBgClr = db_column_text(&q, 8); const char *zBr = db_column_text(&q, 9); int fmid = db_column_int(&q, 10); int pfnid = db_column_int(&q, 11); int gidx; char zTime[10]; int nParent = 0; int aParent[GR_MAX_RAIL]; static Stmt qparent; if( baseCheckin && frid && !bag_find(&ancestor, frid) ) continue; db_static_prepare(&qparent, "SELECT DISTINCT pid FROM mlink" " WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid" " ORDER BY isaux /*sort*/" ); db_bind_int(&qparent, ":fid", frid); db_bind_int(&qparent, ":mid", fmid); db_bind_int(&qparent, ":fnid", fnid); while( db_step(&qparent)==SQLITE_ROW && nParent<ArraySize(aParent) ){ aParent[nParent] = db_column_int(&qparent, 0); if( baseCheckin ) bag_insert(&ancestor, aParent[nParent]); nParent++; } db_reset(&qparent); if( zBr==0 ) zBr = "trunk"; if( uBg ){ zBgClr = hash_color(zUser); }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){ zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr); } gidx = graph_add_row(pGraph, frid>0 ? frid : fpid+1000000000, nParent, aParent, zBr, zBgClr, zUuid, 0); if( strncmp(zDate, zPrevDate, 10) ){ sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate); @ <tr><td> @ <div class="divider timelineDate">%s(zPrevDate)</div> @ </td><td></td><td></td></tr> } memcpy(zTime, &zDate[11], 5); zTime[5] = 0; @ <tr><td class="timelineTime"> @ %z(href("%R/timeline?c=%t",zDate))%s(zTime)</a></td> @ <td class="timelineGraph"><div id="m%d(gidx)"></div></td> if( zBgClr && zBgClr[0] ){ @ <td class="timelineTableCell" style="background-color: %h(zBgClr);"> }else{ @ <td class="timelineTableCell"> } if( zUuid ){ if( nParent==0 ){ @ <b>Added</b> }else if( pfnid ){ char *zPrevName = db_text(0, "SELECT name FROM filename WHERE fnid=%d", pfnid); @ <b>Renamed</b> from @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a> } @ %z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a> if( fShowId ){ @ (%d(frid)) } @ part of check-in }else{ char *zNewName; zNewName = db_text(0, "SELECT name FROM filename WHERE fnid = " " (SELECT fnid FROM mlink" " WHERE mid=%d" " AND pfnid IN (SELECT fnid FROM filename WHERE name=%Q))", fmid, zFilename); if( zNewName ){ @ <b>Renamed</b> to @ %z(href("%R/finfo?name=%t",zNewName))%h(zNewName)</a> by check-in fossil_free(zNewName); }else{ @ <b>Deleted</b> by check-in } } hyperlink_to_uuid(zCkin); if( fShowId ){ @ (%d(fmid)) } @ %W(zCom) (user: hyperlink_to_user(zUser, zDate, ""); @ branch: %z(href("%R/timeline?t=%T&n=200",zBr))%h(zBr)</a>) if( g.perm.Hyperlink && zUuid ){ const char *z = zFilename; @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin)) @ [annotate]</a> @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin)) @ [blame]</a> @ %z(href("%R/timeline?n=200&uf=%!S",zUuid))[check-ins using]</a> if( fpid ){ @ %z(href("%R/fdiff?sbs=1&v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a> } } if( fDebug & FINFO_DEBUG_MLINK ){ int ii; char *zAncLink; @ <br>fid=%d(frid) pid=%d(fpid) mid=%d(fmid) if( nParent>0 ){ @ parents=%d(aParent[0]) for(ii=1; ii<nParent; ii++){ @ %d(aParent[ii]) } } zAncLink = href("%R/finfo?name=%T&ci=%!S&debug=1",zFilename,zCkin); @ %z(zAncLink)[ancestry]</a> } tag_private_status(frid); @ </td></tr> } db_finalize(&q); if( pGraph ){ graph_finish(pGraph, 1); if( pGraph->nErr ){ graph_free(pGraph); pGraph = 0; }else{ int w = pGraph->mxRail*pGraph->iRailPitch + 28; @ <tr><td></td><td> @ <div id="grbtm" style="width:%d(w)px;"></div> @ </td><td></td></tr> } } @ </table> timeline_output_graph_javascript(pGraph, 0, 1); style_footer(); } |
Added src/foci.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This routine implements an SQLite virtual table that gives all of the ** files associated with a single check-in. ** ** The filename "foci" is short for "Files of Check-in". ** ** Usage example: ** ** CREATE VIRTUAL TABLE temp.foci USING files_of_checkin; ** -- ^^^^--- important! ** SELECT * FROM foci WHERE checkinID=symbolic_name_to_rid('trunk'); ** ** The symbolic_name_to_rid('trunk') function finds the BLOB.RID value ** corresponding to the 'trunk' tag. Then the files_of_checkin virtual table ** decodes the manifest defined by that BLOB and returns all files described ** by that manifest. The "schema" for the temp.foci table is: ** ** CREATE TABLE files_of_checkin( ** checkinID INTEGER, -- RID for the check-in manifest ** filename TEXT, -- Name of a file ** uuid TEXT, -- SHA1 hash of the file ** previousName TEXT, -- Name of the file in previous check-in ** perm TEXT -- Permissions on the file ** ); ** */ #include "config.h" #include "foci.h" #include <assert.h> /* ** The schema for the virtual table: */ static const char zFociSchema[] = @ CREATE TABLE files_of_checkin( @ checkinID INTEGER, -- RID for the check-in manifest @ filename TEXT, -- Name of a file @ uuid TEXT, -- SHA1 hash of the file @ previousName TEXT, -- Name of the file in previous check-in @ perm TEXT -- Permissions on the file @ ); ; #if INTERFACE /* ** The subclasses of sqlite3_vtab and sqlite3_vtab_cursor tables ** that implement the files_of_checkin virtual table. */ struct FociTable { sqlite3_vtab base; /* Base class - must be first */ }; struct FociCursor { sqlite3_vtab_cursor base; /* Base class - must be first */ Manifest *pMan; /* Current manifest */ ManifestFile *pFile; /* Current file */ int iFile; /* File index */ }; #endif /* INTERFACE */ /* ** Connect to or create a foci virtual table. */ static int fociConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ FociTable *pTab; pTab = (FociTable *)sqlite3_malloc(sizeof(FociTable)); memset(pTab, 0, sizeof(FociTable)); sqlite3_declare_vtab(db, zFociSchema); *ppVtab = &pTab->base; return SQLITE_OK; } /* ** Disconnect from or destroy a focivfs virtual table. */ static int fociDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* ** Available scan methods: ** ** (0) A full scan. Visit every manifest in the repo. (Slow) ** (1) checkinID=?. visit only the single manifest specifed. */ static int fociBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int i; pIdxInfo->estimatedCost = 10000.0; for(i=0; i<pIdxInfo->nConstraint; i++){ if( pIdxInfo->aConstraint[i].iColumn==0 && pIdxInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ ){ pIdxInfo->idxNum = 1; pIdxInfo->estimatedCost = 1.0; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; break; } } return SQLITE_OK; } /* ** Open a new focivfs cursor. */ static int fociOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ FociCursor *pCsr; pCsr = (FociCursor *)sqlite3_malloc(sizeof(FociCursor)); memset(pCsr, 0, sizeof(FociCursor)); pCsr->base.pVtab = pVTab; *ppCursor = (sqlite3_vtab_cursor *)pCsr; return SQLITE_OK; } /* ** Close a focivfs cursor. */ static int fociClose(sqlite3_vtab_cursor *pCursor){ FociCursor *pCsr = (FociCursor *)pCursor; manifest_destroy(pCsr->pMan); sqlite3_free(pCsr); return SQLITE_OK; } /* ** Move a focivfs cursor to the next entry in the file. */ static int fociNext(sqlite3_vtab_cursor *pCursor){ FociCursor *pCsr = (FociCursor *)pCursor; pCsr->pFile = manifest_file_next(pCsr->pMan, 0); pCsr->iFile++; return SQLITE_OK; } static int fociEof(sqlite3_vtab_cursor *pCursor){ FociCursor *pCsr = (FociCursor *)pCursor; return pCsr->pFile==0; } static int fociFilter( sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ FociCursor *pCur = (FociCursor *)pCursor; manifest_destroy(pCur->pMan); if( idxNum ){ pCur->pMan = manifest_get(sqlite3_value_int(argv[0]), CFTYPE_MANIFEST, 0); pCur->iFile = 0; manifest_file_rewind(pCur->pMan); pCur->pFile = manifest_file_next(pCur->pMan, 0); }else{ pCur->pMan = 0; pCur->iFile = 0; } return SQLITE_OK; } static int fociColumn( sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i ){ FociCursor *pCsr = (FociCursor *)pCursor; switch( i ){ case 0: /* checkinID */ sqlite3_result_int(ctx, pCsr->pMan->rid); break; case 1: /* filename */ sqlite3_result_text(ctx, pCsr->pFile->zName, -1, SQLITE_TRANSIENT); break; case 2: /* uuid */ sqlite3_result_text(ctx, pCsr->pFile->zUuid, -1, SQLITE_TRANSIENT); break; case 3: /* previousName */ sqlite3_result_text(ctx, pCsr->pFile->zPrior, -1, SQLITE_TRANSIENT); break; case 4: /* perm */ sqlite3_result_text(ctx, pCsr->pFile->zPerm, -1, SQLITE_TRANSIENT); break; } return SQLITE_OK; } static int fociRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ FociCursor *pCsr = (FociCursor *)pCursor; *pRowid = pCsr->iFile; return SQLITE_OK; } int foci_register(sqlite3 *db){ static sqlite3_module foci_module = { 0, /* iVersion */ fociConnect, /* xCreate */ fociConnect, /* xConnect */ fociBestIndex, /* xBestIndex */ fociDisconnect, /* xDisconnect */ fociDisconnect, /* xDestroy */ fociOpen, /* xOpen - open a cursor */ fociClose, /* xClose - close a cursor */ fociFilter, /* xFilter - configure scan constraints */ fociNext, /* xNext - advance a cursor */ fociEof, /* xEof - check for end of scan */ fociColumn, /* xColumn - read data */ fociRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; sqlite3_create_module(db, "files_of_checkin", &foci_module, 0); return SQLITE_OK; } |
Added src/fusefs.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@sqlite.org ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This module implements the userspace side of a Fuse Filesystem that ** contains all check-ins for a fossil repository. ** ** This module is a mostly a no-op unless compiled with -DFOSSIL_HAVE_FUSEFS. ** The FOSSIL_HAVE_FUSEFS should be omitted on systems that lack support for ** the Fuse Filesystem, of course. */ #include "config.h" #include <stdio.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include "fusefs.h" #ifdef FOSSIL_HAVE_FUSEFS #define FUSE_USE_VERSION 26 #include <fuse.h> /* ** Global state information about the archive */ static struct sGlobal { /* A cache of a single check-in manifest */ int rid; /* rid for the cached manifest */ char *zSymName; /* Symbolic name corresponding to rid */ Manifest *pMan; /* The cached manifest */ /* A cache of a single file within a single check-in */ int iFileRid; /* Check-in ID for the cached file */ ManifestFile *pFile; /* Name of a cached file */ Blob content; /* Content of the cached file */ /* Parsed path */ char *az[3]; /* 0=type, 1=id, 2=path */ } fusefs; /* ** Clear the fusefs.sz[] array. */ static void fusefs_clear_path(void){ int i; for(i=0; i<count(fusefs.az); i++){ fossil_free(fusefs.az[i]); fusefs.az[i] = 0; } } /* ** Split of the input path into 0, 1, 2, or 3 elements in fusefs.az[]. ** Return the number of elements. ** ** Any prior path parse is deleted. */ static int fusefs_parse_path(const char *zPath){ int i, j; fusefs_clear_path(); if( strcmp(zPath,"/")==0 ) return 0; for(i=0, j=1; i<2 && zPath[j]; i++){ int jStart = j; while( zPath[j] && zPath[j]!='/' ){ j++; } fusefs.az[i] = mprintf("%.*s", j-jStart, &zPath[jStart]); if( zPath[j] ) j++; } if( zPath[j] ) fusefs.az[i++] = fossil_strdup(&zPath[j]); return i; } /* ** Reclaim memory used by the fusefs local variable. */ static void fusefs_reset(void){ blob_reset(&fusefs.content); manifest_destroy(fusefs.pMan); fusefs.pMan = 0; fossil_free(fusefs.zSymName); fusefs.zSymName = 0; fusefs.pFile = 0; } /* ** Load manifest rid into the cache. */ static void fusefs_load_rid(int rid, const char *zSymName){ if( fusefs.rid==rid && fusefs.pMan!=0 ) return; fusefs_reset(); fusefs.zSymName = fossil_strdup(zSymName); fusefs.pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); fusefs.rid = rid; } /* ** Locate the rid corresponding to a symbolic name */ static int fusefs_name_to_rid(const char *zSymName){ if( fusefs.rid>0 && strcmp(zSymName, fusefs.zSymName)==0 ){ return fusefs.rid; }else{ return symbolic_name_to_rid(zSymName, "ci"); } } /* ** Implementation of stat() */ static int fusefs_getattr(const char *zPath, struct stat *stbuf){ int n, rid; ManifestFile *pFile; char *zDir; stbuf->st_uid = getuid(); stbuf->st_gid = getgid(); n = fusefs_parse_path(zPath); if( n==0 ){ stbuf->st_mode = S_IFDIR | 0555; stbuf->st_nlink = 2; return 0; } if( strcmp(fusefs.az[0],"checkins")!=0 ) return -ENOENT; if( n==1 ){ stbuf->st_mode = S_IFDIR | 0111; stbuf->st_nlink = 2; return 0; } rid = fusefs_name_to_rid(fusefs.az[1]); if( rid<=0 ) return -ENOENT; if( n==2 ){ stbuf->st_mode = S_IFDIR | 0555; stbuf->st_nlink = 2; return 0; } fusefs_load_rid(rid, fusefs.az[1]); if( fusefs.pMan==0 ) return -ENOENT; stbuf->st_mtime = (fusefs.pMan->rDate - 2440587.5)*86400.0; pFile = manifest_file_seek(fusefs.pMan, fusefs.az[2], 0); if( pFile ){ static Stmt q; stbuf->st_mode = S_IFREG | (manifest_file_mperm(pFile)==PERM_EXE ? 0555 : 0444); stbuf->st_nlink = 1; db_static_prepare(&q, "SELECT size FROM blob WHERE uuid=$uuid"); db_bind_text(&q, "$uuid", pFile->zUuid); if( db_step(&q)==SQLITE_ROW ){ stbuf->st_size = db_column_int(&q, 0); } db_reset(&q); return 0; } zDir = mprintf("%s/", fusefs.az[2]); pFile = manifest_file_seek(fusefs.pMan, zDir, 1); fossil_free(zDir); if( pFile==0 ) return -ENOENT; n = (int)strlen(fusefs.az[2]); if( strncmp(fusefs.az[2], pFile->zName, n)!=0 ) return -ENOENT; if( pFile->zName[n]!='/' ) return -ENOENT; stbuf->st_mode = S_IFDIR | 0555; stbuf->st_nlink = 2; return 0; } /* ** Implementation of readdir() */ static int fusefs_readdir( const char *zPath, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi ){ int n, rid; ManifestFile *pFile; const char *zPrev = ""; int nPrev = 0; char *z; int cnt = 0; n = fusefs_parse_path(zPath); if( n==0 ){ filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); filler(buf, "checkins", NULL, 0); return 0; } if( strcmp(fusefs.az[0],"checkins")!=0 ) return -ENOENT; if( n==1 ) return -ENOENT; rid = fusefs_name_to_rid(fusefs.az[1]); if( rid<=0 ) return -ENOENT; fusefs_load_rid(rid, fusefs.az[1]); if( fusefs.pMan==0 ) return -ENOENT; filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); manifest_file_rewind(fusefs.pMan); if( n==2 ){ while( (pFile = manifest_file_next(fusefs.pMan, 0))!=0 ){ if( nPrev>0 && strncmp(pFile->zName, zPrev, nPrev)==0 ) continue; zPrev = pFile->zName; for(nPrev=0; zPrev[nPrev] && zPrev[nPrev]!='/'; nPrev++){} z = mprintf("%.*s", nPrev, zPrev); filler(buf, z, NULL, 0); fossil_free(z); cnt++; } }else{ char *zBase = mprintf("%s/", fusefs.az[2]); int nBase = (int)strlen(zBase); while( (pFile = manifest_file_next(fusefs.pMan, 0))!=0 ){ if( strcmp(pFile->zName, zBase)>=0 ) break; } while( pFile && strncmp(zBase, pFile->zName, nBase)==0 ){ if( nPrev==0 || strncmp(pFile->zName+nBase, zPrev, nPrev)!=0 ){ zPrev = pFile->zName+nBase; for(nPrev=0; zPrev[nPrev] && zPrev[nPrev]!='/'; nPrev++){} if( zPrev[nPrev]=='/' ){ z = mprintf("%.*s", nPrev, zPrev); filler(buf, z, NULL, 0); fossil_free(z); }else{ filler(buf, zPrev, NULL, 0); nPrev = 0; } cnt++; } pFile = manifest_file_next(fusefs.pMan, 0); } fossil_free(zBase); } return cnt>0 ? 0 : -ENOENT; } /* ** Implementation of read() */ static int fusefs_read( const char *zPath, char *buf, size_t size, off_t offset, struct fuse_file_info *fi ){ int n, rid; n = fusefs_parse_path(zPath); if( n<3 ) return -ENOENT; if( strcmp(fusefs.az[0], "checkins")!=0 ) return -ENOENT; rid = fusefs_name_to_rid(fusefs.az[1]); if( rid<=0 ) return -ENOENT; fusefs_load_rid(rid, fusefs.az[1]); if( fusefs.pFile!=0 && strcmp(fusefs.az[2], fusefs.pFile->zName)!=0 ){ fusefs.pFile = 0; blob_reset(&fusefs.content); } fusefs.pFile = manifest_file_seek(fusefs.pMan, fusefs.az[2], 0); if( fusefs.pFile==0 ) return -ENOENT; rid = uuid_to_rid(fusefs.pFile->zUuid, 0); blob_reset(&fusefs.content); content_get(rid, &fusefs.content); if( offset>blob_size(&fusefs.content) ) return 0; if( offset+size>blob_size(&fusefs.content) ){ size = blob_size(&fusefs.content) - offset; } memcpy(buf, blob_buffer(&fusefs.content)+offset, size); return size; } static struct fuse_operations fusefs_methods = { .getattr = fusefs_getattr, .readdir = fusefs_readdir, .read = fusefs_read, }; #endif /* FOSSIL_HAVE_FUSEFS */ /* ** COMMAND: fusefs ** ** Usage: %fossil fusefs [--debug] DIRECTORY ** ** This command uses the Fuse Filesystem to mount a directory at ** DIRECTORY that contains the content of all check-ins in the ** repository. The names of files are DIRECTORY/checkins/VERSION/PATH ** where DIRECTORY is the root of the mount, VERSION is any valid ** check-in name (examples: "trunk" or "tip" or a tag or any unique ** prefix of a SHA1 hash, etc) and PATH is the pathname of the file ** in the check-in. If DIRECTORY does not exist, then an attempt is ** made to create it. ** ** The DIRECTORY/checkins directory is not searchable so one cannot ** do "ls DIRECTORY/checkins" to get a listing of all possible check-in ** names. There are countless variations on check-in names and it is ** impractical to list them all. But all other directories are searchable ** and so the "ls" command will work everywhere else in the fusefs ** file hierarchy. ** ** The FuseFS typically only works on Linux, and then only on Linux ** systems that have the right kernel drivers and have install the ** appropriate support libraries. ** ** After stopping the "fossil fusefs" command, it might also be necessary ** to run "fusermount -u DIRECTORY" to reset the FuseFS before using it ** again. */ void fusefs_cmd(void){ #ifndef FOSSIL_HAVE_FUSEFS fossil_fatal("this build of fossil does not support the fuse filesystem"); #else char *zMountPoint; char *azNewArgv[5]; int doDebug = find_option("debug","d",0)!=0; db_find_and_open_repository(0,0); verify_all_options(); blob_init(&fusefs.content, 0, 0); if( g.argc!=3 ) usage("DIRECTORY"); zMountPoint = g.argv[2]; if( file_mkdir(zMountPoint, 0) ){ fossil_fatal("cannot make directory [%s]", zMountPoint); } azNewArgv[0] = g.argv[0]; azNewArgv[1] = doDebug ? "-d" : "-f"; azNewArgv[2] = "-s"; azNewArgv[3] = zMountPoint; azNewArgv[4] = 0; g.localOpen = 0; /* Prevent tags like "current" and "prev" */ fuse_main(4, azNewArgv, &fusefs_methods, NULL); fusefs_reset(); fusefs_clear_path(); #endif } |
Changes to src/glob.c.
︙ | ︙ | |||
37 38 39 40 41 42 43 | ** themselves. ** ** This routine makes no effort to free the memory space it uses, which ** currently consists of a blob object and its contents. */ char *glob_expr(const char *zVal, const char *zGlobList){ Blob expr; | | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | ** themselves. ** ** This routine makes no effort to free the memory space it uses, which ** currently consists of a blob object and its contents. */ char *glob_expr(const char *zVal, const char *zGlobList){ Blob expr; const char *zSep = "("; int nTerm = 0; int i; int cTerm; if( zGlobList==0 || zGlobList[0]==0 ) return fossil_strdup("0"); blob_zero(&expr); while( zGlobList[0] ){ while( fossil_isspace(zGlobList[0]) || zGlobList[0]==',' ){ zGlobList++; /* Skip leading commas, spaces, and newlines */ } if( zGlobList[0]==0 ) break; if( zGlobList[0]=='\'' || zGlobList[0]=='"' ){ |
︙ | ︙ | |||
71 72 73 74 75 76 77 | if( zGlobList[0] ) zGlobList++; nTerm++; } if( nTerm ){ blob_appendf(&expr, ")"); return blob_str(&expr); }else{ | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | if( zGlobList[0] ) zGlobList++; nTerm++; } if( nTerm ){ blob_appendf(&expr, ")"); return blob_str(&expr); }else{ return fossil_strdup("0"); } } #if INTERFACE /* ** A Glob object holds a set of patterns read to be matched against ** a string. |
︙ | ︙ | |||
136 137 138 139 140 141 142 | if( z[i]==0 ) break; z[i] = 0; z += i+1; } return p; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | if( z[i]==0 ) break; z[i] = 0; z += i+1; } return p; } /* ** Return true (non-zero) if zString matches any of the patterns in ** the Glob. The value returned is actually a 1-based index of the pattern ** that matched. Return 0 if none of the patterns match zString. ** ** A NULL glob matches nothing. */ int glob_match(Glob *pGlob, const char *zString){ int i; if( pGlob==0 ) return 0; for(i=0; i<pGlob->nPattern; i++){ if( sqlite3_strglob(pGlob->azPattern[i], zString)==0 ) return i+1; } return 0; } /* ** Free all memory associated with the given Glob object */ |
︙ | ︙ |
Changes to src/graph.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | */ struct GraphRow { int rid; /* The rid for the check-in */ i8 nParent; /* Number of parents */ int *aParent; /* Array of parents. 0 element is primary .*/ char *zBranch; /* Branch name */ char *zBgClr; /* Background Color */ | | | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | */ struct GraphRow { int rid; /* The rid for the check-in */ i8 nParent; /* Number of parents */ int *aParent; /* Array of parents. 0 element is primary .*/ char *zBranch; /* Branch name */ char *zBgClr; /* Background Color */ char zUuid[41]; /* Check-in for file ID */ GraphRow *pNext; /* Next row down in the list of all rows */ GraphRow *pPrev; /* Previous row */ int idx; /* Row index. First is 1. 0 used for "none" */ int idxTop; /* Direct descendent highest up on the graph */ GraphRow *pChild; /* Child immediately above this node */ u8 isDup; /* True if this is duplicate of a prior entry */ u8 isLeaf; /* True if this is a leaf node */ u8 timeWarp; /* Child is earlier in time */ u8 bDescender; /* True if riser from bottom of graph to here. */ i8 iRail; /* Which rail this check-in appears on. 0-based.*/ i8 mergeOut; /* Merge out on rail mergeOut/4. -1 for none */ u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */ int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */ int mergeUpto; /* Draw the mergeOut rail up to this level */ u64 mergeDown; /* Draw merge lines up from bottom of graph */ u64 railInUse; /* Mask of occupied rails at this row */ }; |
︙ | ︙ | |||
152 153 154 155 156 157 158 | /* ** Return the canonical pointer for a given branch name. ** Multiple calls to this routine with equivalent strings ** will return the same pointer. ** ** The returned value is a pointer to a (readonly) string that | | | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | /* ** Return the canonical pointer for a given branch name. ** Multiple calls to this routine with equivalent strings ** will return the same pointer. ** ** The returned value is a pointer to a (readonly) string that ** has the useful property that strings can be checked for ** equality by comparing pointers. ** ** Note: also used for background color names. */ static char *persistBranchName(GraphContext *p, const char *zBranch){ int i; for(i=0; i<p->nBranch; i++){ |
︙ | ︙ | |||
212 213 214 215 216 217 218 | p->nRow++; pRow->idx = pRow->idxTop = p->nRow; return pRow->idx; } /* ** Return the index of a rail currently not in use for any row between | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | p->nRow++; pRow->idx = pRow->idxTop = p->nRow; return pRow->idx; } /* ** Return the index of a rail currently not in use for any row between ** top and bottom, inclusive. */ static int findFreeRail( GraphContext *p, /* The graph context */ int top, int btm, /* Span of rows for which the rail is needed */ u64 inUseMask, /* Mask or rails already in use */ int iNearto /* Find rail nearest to this rail */ ){ |
︙ | ︙ | |||
361 362 363 364 365 366 367 | } p->mxRail = -1; /* Purge merge-parents that are out-of-graph if descenders are not ** drawn. ** ** Each node has one primary parent and zero or more "merge" parents. | | > > > > > > > > > > > > > > > > > | > > > > | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | } p->mxRail = -1; /* Purge merge-parents that are out-of-graph if descenders are not ** drawn. ** ** Each node has one primary parent and zero or more "merge" parents. ** A merge parent is a prior check-in from which changes were merged into ** the current check-in. If a merge parent is not in the visible section ** of this graph, then no arrows will be drawn for it, so remove it from ** the aParent[] array. */ if( omitDescenders ){ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ for(i=1; i<pRow->nParent; i++){ if( hashFind(p, pRow->aParent[i])==0 ){ pRow->aParent[i] = pRow->aParent[--pRow->nParent]; i--; } } } } /* If the primary parent is in a different branch, but there are ** other parents in the same branch, reorder the parents to make ** the parent from the same branch the primary parent. */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ if( pRow->isDup ) continue; if( pRow->nParent<2 ) continue; /* Not a fork */ pParent = hashFind(p, pRow->aParent[0]); if( pParent==0 ) continue; /* Parent off-screen */ if( pParent->zBranch==pRow->zBranch ) continue; /* Same branch */ for(i=1; i<pRow->nParent; i++){ pParent = hashFind(p, pRow->aParent[i]); if( pParent && pParent->zBranch==pRow->zBranch ){ int t = pRow->aParent[0]; pRow->aParent[0] = pRow->aParent[i]; pRow->aParent[i] = t; break; } } } /* Find the pChild pointer for each node. ** ** The pChild points to the node directly above on the same rail. ** The pChild must be in the same branch. Leaf nodes have a NULL ** pChild. ** ** In the case of a fork, choose the pChild that results in the ** longest rail. |
︙ | ︙ | |||
531 532 533 534 535 536 537 | createMergeRiser(p, pDesc, pRow); if( p->mxRail>=GR_MAX_RAIL ) return; } } } /* | | | 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 | createMergeRiser(p, pDesc, pRow); if( p->mxRail>=GR_MAX_RAIL ) return; } } } /* ** Insert merge rails from primaries to duplicates. */ if( hasDup ){ int dupRail; int mxRail; find_max_rail(p); mxRail = p->mxRail; dupRail = mxRail+1; |
︙ | ︙ | |||
561 562 563 564 565 566 567 | if( mxRail>=GR_MAX_RAIL ) return; } /* ** Find the maximum rail number. */ find_max_rail(p); | > > | | > | 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | if( mxRail>=GR_MAX_RAIL ) return; } /* ** Find the maximum rail number. */ find_max_rail(p); p->iRailPitch = atoi(PD("railpitch","0")); if( p->iRailPitch<=0 ){ p->iRailPitch = 18 - (p->mxRail/3); if( p->iRailPitch<11 ) p->iRailPitch = 11; } p->nErr = 0; } |
Changes to src/gzip.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 | ** ** This file contains code used to incrementally generate a GZIP compressed ** file. The GZIP format is described in RFC-1952. ** ** State information is stored in static variables, so this implementation ** can only be building up a single GZIP file at a time. */ #include <assert.h> | > > > > > | < > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | ** ** This file contains code used to incrementally generate a GZIP compressed ** file. The GZIP format is described in RFC-1952. ** ** State information is stored in static variables, so this implementation ** can only be building up a single GZIP file at a time. */ #include "config.h" #include <assert.h> #if defined(FOSSIL_ENABLE_MINIZ) # define MINIZ_HEADER_FILE_ONLY # include "miniz.c" #else # include <zlib.h> #endif #include "gzip.h" /* ** State information for the GZIP file under construction. */ struct gzip_state { int eState; /* 0: idle 1: header 2: compressing */ |
︙ | ︙ | |||
53 54 55 56 57 58 59 | char aHdr[10]; assert( gzip.eState==0 ); blob_zero(&gzip.out); aHdr[0] = 0x1f; aHdr[1] = 0x8b; aHdr[2] = 8; aHdr[3] = 0; | | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | char aHdr[10]; assert( gzip.eState==0 ); blob_zero(&gzip.out); aHdr[0] = 0x1f; aHdr[1] = 0x8b; aHdr[2] = 8; aHdr[3] = 0; if( now==-1 ){ now = db_int64(0, "SELECT (julianday('now') - 2440587.5)*86400.0"); } put32(&aHdr[4], now&0xffffffff); aHdr[8] = 2; aHdr[9] = 255; blob_append(&gzip.out, aHdr, 10); gzip.iCRC = 0; gzip.eState = 1; } /* ** Add nIn bytes of content from pIn to the gzip file. */ #define GZIP_BUFSZ 100000 void gzip_step(const char *pIn, int nIn){ char *zOutBuf; int nOut; nOut = nIn + nIn/10 + 100; if( nOut<100000 ) nOut = 100000; zOutBuf = fossil_malloc(nOut); gzip.stream.avail_in = nIn; gzip.stream.next_in = (unsigned char*)pIn; gzip.stream.avail_out = nOut; gzip.stream.next_out = (unsigned char*)zOutBuf; |
︙ | ︙ | |||
124 125 126 127 128 129 130 | ** Compress a file using gzip. */ void test_gzip_cmd(void){ Blob b; char *zOut; if( g.argc!=3 ) usage("FILENAME"); sqlite3_open(":memory:", &g.db); | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | ** Compress a file using gzip. */ void test_gzip_cmd(void){ Blob b; char *zOut; if( g.argc!=3 ) usage("FILENAME"); sqlite3_open(":memory:", &g.db); gzip_begin(-1); blob_read_from_file(&b, g.argv[2]); zOut = mprintf("%s.gz", g.argv[2]); gzip_step(blob_buffer(&b), blob_size(&b)); blob_reset(&b); gzip_finish(&b); blob_write_to_file(&b, zOut); blob_reset(&b); fossil_free(zOut); } |
Changes to src/http.c.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | ** ** This file contains code that implements the client-side HTTP protocol */ #include "config.h" #include "http.h" #include <assert.h> /* ** Construct the "login" card with the client credentials. ** ** login LOGIN NONCE SIGNATURE ** ** The LOGIN is the user id of the client. NONCE is the sha1 checksum ** of all payload that follows the login card. SIGNATURE is the sha1 ** checksum of the nonce followed by the user password. ** ** Write the constructed login card into pLogin. pLogin is initialized ** by this routine. */ static void http_build_login_card(Blob *pPayload, Blob *pLogin){ Blob nonce; /* The nonce */ const char *zLogin; /* The user login name */ const char *zPw; /* The user password */ Blob pw; /* The nonce with user password appended */ Blob sig; /* The signature field */ blob_zero(pLogin); | > > > > > > > > > > > > > > > > | | | | | | < < < < < < | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | ** ** This file contains code that implements the client-side HTTP protocol */ #include "config.h" #include "http.h" #include <assert.h> #ifdef _WIN32 #include <io.h> #ifndef isatty #define isatty(d) _isatty(d) #endif #ifndef fileno #define fileno(s) _fileno(s) #endif #endif /* Maximum number of HTTP Authorization attempts */ #define MAX_HTTP_AUTH 2 /* Keep track of HTTP Basic Authorization failures */ static int fSeenHttpAuth = 0; /* ** Construct the "login" card with the client credentials. ** ** login LOGIN NONCE SIGNATURE ** ** The LOGIN is the user id of the client. NONCE is the sha1 checksum ** of all payload that follows the login card. SIGNATURE is the sha1 ** checksum of the nonce followed by the user password. ** ** Write the constructed login card into pLogin. pLogin is initialized ** by this routine. */ static void http_build_login_card(Blob *pPayload, Blob *pLogin){ Blob nonce; /* The nonce */ const char *zLogin; /* The user login name */ const char *zPw; /* The user password */ Blob pw; /* The nonce with user password appended */ Blob sig; /* The signature field */ blob_zero(pLogin); if( g.url.user==0 || fossil_strcmp(g.url.user, "anonymous")==0 ){ return; /* If no login card for users "nobody" and "anonymous" */ } if( g.url.isSsh ){ return; /* If no login card for SSH: */ } blob_zero(&nonce); blob_zero(&pw); sha1sum_blob(pPayload, &nonce); blob_copy(&pw, &nonce); zLogin = g.url.user; if( g.url.passwd ){ zPw = g.url.passwd; }else if( g.cgiOutput ){ /* Password failure while doing a sync from the web interface */ cgi_printf("*** incorrect or missing password for user %h\n", zLogin); zPw = 0; }else{ /* Password failure while doing a sync from the command-line interface */ url_prompt_for_password(); zPw = g.url.passwd; } /* The login card wants the SHA1 hash of the password, so convert the ** password to its SHA1 hash if it isn't already a SHA1 hash. */ /* fossil_print("\nzPw=[%s]\n", zPw); // TESTING ONLY */ if( zPw && zPw[0] ) zPw = sha1_shared_secret(zPw, zLogin, 0); blob_append(&pw, zPw, -1); sha1sum_blob(&pw, &sig); blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig); |
︙ | ︙ | |||
90 91 92 93 94 95 96 | ** the complete payload (including the login card) already compressed. */ static void http_build_header(Blob *pPayload, Blob *pHdr){ int i; const char *zSep; blob_zero(pHdr); | | | | | | | | < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | ** the complete payload (including the login card) already compressed. */ static void http_build_header(Blob *pPayload, Blob *pHdr){ int i; const char *zSep; blob_zero(pHdr); i = strlen(g.url.path); if( i>0 && g.url.path[i-1]=='/' ){ zSep = ""; }else{ zSep = "/"; } blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.url.path, zSep); if( g.url.proxyAuth ){ blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth); } if( g.zHttpAuth && g.zHttpAuth[0] ){ const char *zCredentials = g.zHttpAuth; char *zEncoded = encode64(zCredentials, -1); blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); fossil_free(zEncoded); } blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname); blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent()); if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); if( g.fHttpTrace ){ blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); }else{ blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); } blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload)); } /* ** Use Fossil credentials for HTTP Basic Authorization prompt */ static int use_fossil_creds_for_httpauth_prompt(void){ Blob x; char c; prompt_user("Use Fossil username and password (y/N)? ", &x); c = blob_str(&x)[0]; blob_reset(&x); return ( c=='y' || c=='Y' ); } /* ** Prompt to save HTTP Basic Authorization information */ static int save_httpauth_prompt(void){ Blob x; char c; if( (g.url.flags & URL_REMEMBER)==0 ) return 0; prompt_user("Remember Basic Authorization credentials (Y/n)? ", &x); c = blob_str(&x)[0]; blob_reset(&x); return ( c!='n' && c!='N' ); } /* ** Get the HTTP Basic Authorization credentials from the user ** when 401 is received. */ char *prompt_for_httpauth_creds(void){ Blob x; char *zUser; char *zPw; char *zPrompt; char *zHttpAuth = 0; if( !isatty(fileno(stdin)) ) return 0; zPrompt = mprintf("\n%s authorization required by\n%s\n", g.url.isHttps==1 ? "Encrypted HTTPS" : "Unencrypted HTTP", g.url.canonical); fossil_print("%s", zPrompt); free(zPrompt); if ( g.url.user && g.url.passwd && use_fossil_creds_for_httpauth_prompt() ){ zHttpAuth = mprintf("%s:%s", g.url.user, g.url.passwd); }else{ prompt_user("Basic Authorization user: ", &x); zUser = mprintf("%b", &x); zPrompt = mprintf("HTTP password for %b: ", &x); blob_reset(&x); prompt_for_password(zPrompt, &x, 1); zPw = mprintf("%b", &x); zHttpAuth = mprintf("%s:%s", zUser, zPw); free(zUser); free(zPw); free(zPrompt); blob_reset(&x); } if( save_httpauth_prompt() ){ set_httpauth(zHttpAuth); } return zHttpAuth; } /* ** Sign the content in pSend, compress it, and send it to the server ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply ** in pRecv. pRecv is assumed to be uninitialized when ** this routine is called - this routine will initialize it. ** ** The server address is contain in the "g" global structure. The ** url_parse() routine should have been called prior to this routine ** in order to fill this structure appropriately. */ int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){ Blob login; /* The login card */ Blob payload; /* The complete payload including login card */ Blob hdr; /* The HTTP request header */ int closeConnection; /* True to close the connection when done */ int iLength; /* Expected length of the reply payload */ int iRecvLen; /* Received length of the reply payload */ int rc = 0; /* Result code */ int iHttpVersion; /* Which version of HTTP protocol server uses */ char *zLine; /* A single line of the reply header */ int i; /* Loop counter */ int isError = 0; /* True if the reply is an error message */ int isCompressed = 1; /* True if the reply is compressed */ if( transport_open(&g.url) ){ fossil_warning("%s", transport_errmsg(&g.url)); return 1; } /* Construct the login card and prepare the complete payload */ blob_zero(&login); if( useLogin ) http_build_login_card(pSend, &login); if( g.fHttpTrace ){ |
︙ | ︙ | |||
188 189 190 191 192 193 194 | transport_log(out); free(zOutFile); } /* ** Send the request to the server. */ | | | | | | > > > > > > > > > > | > > > > > > > > > > > | | > | > > > | > > | < > > > | | | | > > > > | > > > | | | | | | | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | transport_log(out); free(zOutFile); } /* ** Send the request to the server. */ transport_send(&g.url, &hdr); transport_send(&g.url, &payload); blob_reset(&hdr); blob_reset(&payload); transport_flip(&g.url); /* ** Read and interpret the server reply */ closeConnection = 1; iLength = -1; while( (zLine = transport_receive_line(&g.url))!=0 && zLine[0]!=0 ){ /* printf("[%s]\n", zLine); fflush(stdout); */ if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){ if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; if( rc==401 ){ if( fSeenHttpAuth++ < MAX_HTTP_AUTH ){ if( g.zHttpAuth ){ if( g.zHttpAuth ) free(g.zHttpAuth); } g.zHttpAuth = prompt_for_httpauth_creds(); transport_close(&g.url); return http_exchange(pSend, pReply, useLogin, maxRedirect); } } if( rc!=200 && rc!=301 && rc!=302 ){ int ii; for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} while( zLine[ii]==' ' ) ii++; fossil_warning("server says: %s", &zLine[ii]); goto write_err; } if( iHttpVersion==0 ){ closeConnection = 1; }else{ closeConnection = 0; } }else if( g.url.isSsh && fossil_strnicmp(zLine, "status:", 7)==0 ){ if( sscanf(zLine, "Status: %d", &rc)!=1 ) goto write_err; if( rc!=200 && rc!=301 && rc!=302 ){ int ii; for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} while( zLine[ii]==' ' ) ii++; fossil_warning("server says: %s", &zLine[ii]); goto write_err; } closeConnection = 0; }else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){ for(i=15; fossil_isspace(zLine[i]); i++){} iLength = atoi(&zLine[i]); }else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){ char c; for(i=11; fossil_isspace(zLine[i]); i++){} c = zLine[i]; if( c=='c' || c=='C' ){ closeConnection = 1; }else if( c=='k' || c=='K' ){ closeConnection = 0; } }else if( ( rc==301 || rc==302 ) && fossil_strnicmp(zLine, "location:", 9)==0 ){ int i, j; if ( --maxRedirect == 0){ fossil_warning("redirect limit exceeded"); goto write_err; } for(i=9; zLine[i] && zLine[i]==' '; i++){} if( zLine[i]==0 ){ fossil_warning("malformed redirect: %s", zLine); goto write_err; } j = strlen(zLine) - 1; while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ j -= 4; zLine[j] = 0; } transport_close(&g.url); transport_global_shutdown(&g.url); fossil_print("redirect with status %d to %s\n", rc, &zLine[i]); url_parse(&zLine[i], 0); fSeenHttpAuth = 0; if( g.zHttpAuth ) free(g.zHttpAuth); g.zHttpAuth = get_httpauth(); return http_exchange(pSend, pReply, useLogin, maxRedirect); }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ isCompressed = 0; }else if( fossil_strnicmp(&zLine[14], "application/x-fossil-uncompressed", -1)==0 ){ isCompressed = 0; }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){ isError = 1; } } } if( iLength<0 ){ fossil_warning("server did not reply"); goto write_err; } if( rc!=200 ){ fossil_warning("\"location:\" missing from %d redirect reply", rc); goto write_err; } /* ** Extract the reply payload that follows the header */ blob_zero(pReply); blob_resize(pReply, iLength); iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength); if( iRecvLen != iLength ){ fossil_warning("response truncated: got %d bytes of %d", iRecvLen, iLength); goto write_err; } blob_resize(pReply, iLength); if( isError ){ char *z; int i, j; z = blob_str(pReply); for(i=j=0; z[i]; i++, j++){ if( z[i]=='<' ){ while( z[i] && z[i]!='>' ) i++; if( z[i]==0 ) break; } z[j] = z[i]; } z[j] = 0; fossil_warning("server sends error: %s", z); goto write_err; } if( isCompressed ) blob_uncompress(pReply, pReply); /* ** Close the connection to the server if appropriate. ** ** FIXME: There is some bug in the lower layers that prevents the ** connection from remaining open. The easiest fix for now is to ** simply close and restart the connection for each round-trip. ** ** For SSH we will leave the connection open. */ if( ! g.url.isSsh ) closeConnection = 1; /* FIX ME */ if( closeConnection ){ transport_close(&g.url); }else{ transport_rewind(&g.url); } return 0; /* ** Jump to here if an error is seen. */ write_err: transport_close(&g.url); return 1; } |
Changes to src/http_socket.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file manages low-level client socket communications. The socket ** might be for a simple HTTP request or for an encrypted HTTPS request. ** ** This file implements a singleton. A single client socket may be active ** at a time. State information is stored in static variables. The identity ** of the server is held in global variables that are set by url_parse(). ** | | > > > > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | ** This file manages low-level client socket communications. The socket ** might be for a simple HTTP request or for an encrypted HTTPS request. ** ** This file implements a singleton. A single client socket may be active ** at a time. State information is stored in static variables. The identity ** of the server is held in global variables that are set by url_parse(). ** ** Low-level sockets are abstracted out into this module because they ** are handled different on Unix and windows. */ #ifndef __EXTENSIONS__ # define __EXTENSIONS__ 1 /* IPv6 won't compile on Solaris without this */ #endif #include "config.h" #include "http_socket.h" #if defined(_WIN32) # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0501 # endif # include <winsock2.h> # include <ws2tcpip.h> #else # include <netinet/in.h> # include <arpa/inet.h> # include <sys/socket.h> # include <netdb.h> |
︙ | ︙ | |||
61 62 63 64 65 66 67 | free(socketErrMsg); socketErrMsg = 0; } /* ** Set the socket error message. */ | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | free(socketErrMsg); socketErrMsg = 0; } /* ** Set the socket error message. */ void socket_set_errmsg(const char *zFormat, ...){ va_list ap; socket_clear_errmsg(); va_start(ap, zFormat); socketErrMsg = vmprintf(zFormat, ap); va_end(ap); } |
︙ | ︙ | |||
122 123 124 125 126 127 128 | #endif iSocket = -1; } } /* ** Open a socket connection. The identify of the server is determined | | | | | > | | > > > | > | < | | | < | | < < < < | | | > > > > > > | > > > > | < < < < | < < < < | | | < > | > > > | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | #endif iSocket = -1; } } /* ** Open a socket connection. The identify of the server is determined ** by pUrlData ** ** pUrlDAta->name Name of the server. Ex: www.fossil-scm.org ** pUrlDAta->port TCP/IP port to use. Ex: 80 ** ** Return the number of errors. */ int socket_open(UrlData *pUrlData){ int rc = 0; struct addrinfo *ai = 0; struct addrinfo *p; struct addrinfo hints; char zPort[30]; char zRemote[NI_MAXHOST]; socket_global_init(); memset(&hints, 0, sizeof(struct addrinfo)); assert( iSocket<0 ); hints.ai_family = g.fIPv4 ? AF_INET : AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; sqlite3_snprintf(sizeof(zPort),zPort,"%d", pUrlData->port); rc = getaddrinfo(pUrlData->name, zPort, &hints, &ai); if( rc ){ socket_set_errmsg("getaddrinfo() fails: %s", gai_strerror(rc)); goto end_socket_open; } for(p=ai; p; p=p->ai_next){ iSocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol); if( iSocket<0 ) continue; if( connect(iSocket,p->ai_addr,p->ai_addrlen)<0 ){ socket_close(); continue; } rc = getnameinfo(p->ai_addr, p->ai_addrlen, zRemote, sizeof(zRemote), 0, 0, NI_NUMERICHOST); if( rc ){ socket_set_errmsg("getnameinfo() failed: %s", gai_strerror(rc)); goto end_socket_open; } g.zIpAddr = mprintf("%s", zRemote); break; } if( p==0 ){ socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, pUrlData->port); rc = 1; } #if !defined(_WIN32) signal(SIGPIPE, SIG_IGN); #endif end_socket_open: if( rc && iSocket>=0 ) socket_close(); if( ai ) freeaddrinfo(ai); return rc; } /* ** Send content out over the open socket connection. */ size_t socket_send(void *NotUsed, void *pContent, size_t N){ size_t sent; |
︙ | ︙ | |||
207 208 209 210 211 212 213 | if( got<=0 ) break; total += (size_t)got; N -= (size_t)got; pContent = (void*)&((char*)pContent)[got]; } return total; } | > > > > > > > > > > > > > > > > > > > > > > > > > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | if( got<=0 ) break; total += (size_t)got; N -= (size_t)got; pContent = (void*)&((char*)pContent)[got]; } return total; } /* ** Attempt to resolve pUrlData->name to an IP address and setup g.zIpAddr ** so rcvfrom gets populated. For hostnames with more than one IP (or ** if overridden in ~/.ssh/config) the rcvfrom may not match the host ** to which we connect. */ void socket_ssh_resolve_addr(UrlData *pUrlData){ struct addrinfo *ai = 0; struct addrinfo hints; char zRemote[NI_MAXHOST]; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if( getaddrinfo(pUrlData->name, NULL, &hints, &ai)==0 && ai!=0 && getnameinfo(ai->ai_addr, ai->ai_addrlen, zRemote, sizeof(zRemote), 0, 0, NI_NUMERICHOST)==0 ){ g.zIpAddr = mprintf("%s (%s)", zRemote, pUrlData->name); } if( ai ) freeaddrinfo(ai); if( g.zIpAddr==0 ){ g.zIpAddr = mprintf("%s", pUrlData->name); } } |
Changes to src/http_ssl.c.
︙ | ︙ | |||
39 40 41 42 43 44 45 | /* ** There can only be a single OpenSSL IO connection open at a time. ** State information about that IO is stored in the following ** local variables: */ static int sslIsInit = 0; /* True after global initialization */ | | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | /* ** There can only be a single OpenSSL IO connection open at a time. ** State information about that IO is stored in the following ** local variables: */ static int sslIsInit = 0; /* True after global initialization */ static BIO *iBio = 0; /* OpenSSL I/O abstraction */ static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */ static SSL_CTX *sslCtx; /* SSL context */ static SSL *ssl; /* ** Clear the SSL error message */ static void ssl_clear_errmsg(void){ free(sslErrMsg); sslErrMsg = 0; } /* ** Set the SSL error message. */ void ssl_set_errmsg(const char *zFormat, ...){ va_list ap; ssl_clear_errmsg(); va_start(ap, zFormat); sslErrMsg = vmprintf(zFormat, ap); va_end(ap); } |
︙ | ︙ | |||
80 81 82 83 84 85 86 | ** display a warning message explaining what to do next. */ static int ssl_client_cert_callback(SSL *ssl, X509 **x509, EVP_PKEY **pkey){ fossil_warning("The remote server requested a client certificate for " "authentication. Specify the pathname to a file containing the PEM " "encoded certificate and private key with the --ssl-identity option " "or the ssl-identity setting."); | | | | | | | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | ** display a warning message explaining what to do next. */ static int ssl_client_cert_callback(SSL *ssl, X509 **x509, EVP_PKEY **pkey){ fossil_warning("The remote server requested a client certificate for " "authentication. Specify the pathname to a file containing the PEM " "encoded certificate and private key with the --ssl-identity option " "or the ssl-identity setting."); return 0; /* no cert available */ } /* ** Call this routine once before any other use of the SSL interface. ** This routine does initial configuration of the SSL module. */ void ssl_global_init(void){ const char *zCaSetting = 0, *zCaFile = 0, *zCaDirectory = 0; const char *identityFile; if( sslIsInit==0 ){ SSL_library_init(); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms(); sslCtx = SSL_CTX_new(SSLv23_client_method()); /* Disable SSLv2 and SSLv3 */ SSL_CTX_set_options(sslCtx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); /* Set up acceptable CA root certificates */ zCaSetting = db_get("ssl-ca-location", 0); if( zCaSetting==0 || zCaSetting[0]=='\0' ){ /* CA location not specified, use platform's default certificate store */ X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx)); }else{ /* User has specified a CA location, make sure it exists and use it */ |
︙ | ︙ | |||
127 128 129 130 131 132 133 | } } if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){ fossil_fatal("Failed to use CA root certificates from " "ssl-ca-location '%s'", zCaSetting); } } | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | } } if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){ fossil_fatal("Failed to use CA root certificates from " "ssl-ca-location '%s'", zCaSetting); } } /* Load client SSL identity, preferring the filename specified on the ** command line */ if( g.zSSLIdentity!=0 ){ identityFile = g.zSSLIdentity; }else{ identityFile = db_get("ssl-identity", 0); } |
︙ | ︙ | |||
162 163 164 165 166 167 168 | SSL_CTX_free(sslCtx); ssl_clear_errmsg(); sslIsInit = 0; } } /* | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > | | | | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | < < < < | | | | < | | | | | | | > | > > | | | | | | | | | | | | | > | > | | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | SSL_CTX_free(sslCtx); ssl_clear_errmsg(); sslIsInit = 0; } } /* ** Close the currently open SSL connection. If no connection is open, ** this routine is a no-op. */ void ssl_close(void){ if( iBio!=NULL ){ (void)BIO_reset(iBio); BIO_free_all(iBio); iBio = NULL; } } /* See RFC2817 for details */ static int establish_proxy_tunnel(UrlData *pUrlData, BIO *bio){ int rc, httpVerMin; char *bbuf; Blob snd, reply; int done=0,end=0; blob_zero(&snd); blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname, pUrlData->proxyOrigPort); blob_appendf(&snd, "Host: %s:%d\r\n", pUrlData->hostname, pUrlData->proxyOrigPort); if( pUrlData->proxyAuth ){ blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth); } blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1); blob_appendf(&snd, "User-Agent: %s\r\n", get_user_agent()); blob_append(&snd, "\r\n", 2); BIO_write(bio, blob_buffer(&snd), blob_size(&snd)); blob_reset(&snd); /* Wait for end of reply */ blob_zero(&reply); do{ int len; char buf[256]; len = BIO_read(bio, buf, sizeof(buf)); blob_append(&reply, buf, len); bbuf = blob_buffer(&reply); len = blob_size(&reply); while(end < len) { if(bbuf[end] == '\r') { if(len - end < 4) { /* need more data */ break; } if(memcmp(&bbuf[end], "\r\n\r\n", 4) == 0) { done = 1; break; } } end++; } }while(!done); sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc); blob_reset(&reply); return rc; } /* ** Open an SSL connection. The identify of the server is determined ** as follows: ** ** g.url.name Name of the server. Ex: www.fossil-scm.org ** pUrlData->port TCP/IP port to use. Ex: 80 ** ** Return the number of errors. */ int ssl_open(UrlData *pUrlData){ X509 *cert; int hasSavedCertificate = 0; int trusted = 0; unsigned long e; ssl_global_init(); /* Get certificate for current server from global config and * (if we have it in config) add it to certificate store. */ cert = ssl_get_certificate(pUrlData, &trusted); if ( cert!=NULL ){ X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert); X509_free(cert); hasSavedCertificate = 1; } if( pUrlData->useProxy ){ int rc; BIO *sBio; char *connStr; connStr = mprintf("%s:%d", g.url.name, pUrlData->port); sBio = BIO_new_connect(connStr); free(connStr); if( BIO_do_connect(sBio)<=0 ){ ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)", pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error())); ssl_close(); return 1; } rc = establish_proxy_tunnel(pUrlData, sBio); if( rc<200||rc>299 ){ ssl_set_errmsg("SSL: proxy connect failed with HTTP status code %d", rc); return 1; } pUrlData->path = pUrlData->proxyUrlPath; iBio = BIO_new_ssl(sslCtx, 1); BIO_push(iBio, sBio); }else{ iBio = BIO_new_ssl_connect(sslCtx); } if( iBio==NULL ) { ssl_set_errmsg("SSL: cannot open SSL (%s)", ERR_reason_error_string(ERR_get_error())); return 1; } BIO_get_ssl(iBio, &ssl); #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) if( !SSL_set_tlsext_host_name(ssl, (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) ){ fossil_warning("WARNING: failed to set server name indication (SNI), " "continuing without it.\n"); } #endif SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); if( !pUrlData->useProxy ){ BIO_set_conn_hostname(iBio, pUrlData->name); BIO_set_conn_int_port(iBio, &pUrlData->port); if( BIO_do_connect(iBio)<=0 ){ ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error())); ssl_close(); return 1; } } if( BIO_do_handshake(iBio)<=0 ) { ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", pUrlData->useProxy?pUrlData->hostname:pUrlData->name, pUrlData->useProxy?pUrlData->proxyOrigPort:pUrlData->port, ERR_reason_error_string(ERR_get_error())); ssl_close(); return 1; } /* Check if certificate is valid */ cert = SSL_get_peer_certificate(ssl); if ( cert==NULL ){ ssl_set_errmsg("No SSL certificate was presented by the peer"); ssl_close(); return 1; } if( trusted<=0 && (e = SSL_get_verify_result(ssl)) != X509_V_OK ){ char *desc, *prompt; const char *warning = ""; Blob ans; char cReply; BIO *mem; unsigned char md[32]; unsigned int mdLength = 31; mem = BIO_new(BIO_s_mem()); X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE); BIO_puts(mem, "\n\nIssued By:\n\n"); X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE); BIO_puts(mem, "\n\nSHA1 Fingerprint:\n\n "); if(X509_digest(cert, EVP_sha1(), md, &mdLength)){ int j; for( j = 0; j < mdLength; ++j ) { BIO_printf(mem, " %02x", md[j]); } } BIO_write(mem, "", 1); /* nul-terminate mem buffer */ BIO_get_mem_data(mem, &desc); if( hasSavedCertificate ){ warning = "WARNING: Certificate doesn't match the " "saved certificate for this host!"; } prompt = mprintf("\nSSL verification failed: %s\n" "Certificate received: \n\n%s\n\n%s\n" "Either:\n" " * verify the certificate is correct using the " "SHA1 fingerprint above\n" " * use the global ssl-ca-location setting to specify your CA root\n" " certificates list\n\n" "If you are not expecting this message, answer no and " "contact your server\nadministrator.\n\n" "Accept certificate for host %s (a=always/y/N)? ", X509_verify_cert_error_string(e), desc, warning, pUrlData->useProxy?pUrlData->hostname:pUrlData->name); BIO_free(mem); prompt_user(prompt, &ans); free(prompt); cReply = blob_str(&ans)[0]; blob_reset(&ans); if( cReply!='y' && cReply!='Y' && cReply!='a' && cReply!='A') { X509_free(cert); ssl_set_errmsg("SSL certificate declined"); ssl_close(); return 1; } if( cReply=='a' || cReply=='A') { if ( trusted==0 ){ prompt_user("\nSave this certificate as fully trusted (a=always/N)? ", &ans); cReply = blob_str(&ans)[0]; trusted = ( cReply=='a' || cReply=='A' ); blob_reset(&ans); } ssl_save_certificate(pUrlData, cert, trusted); } } /* Set the Global.zIpAddr variable to the server we are talking to. ** This is used to populate the ipaddr column of the rcvfrom table, ** if any files are received from the server. */ { /* IPv4 only code */ const unsigned char *ip = (const unsigned char *) BIO_get_conn_ip(iBio); g.zIpAddr = mprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); } X509_free(cert); return 0; } /* ** Save certificate to global config. */ void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){ BIO *mem; char *zCert, *zHost; mem = BIO_new(BIO_s_mem()); PEM_write_bio_X509(mem, cert); BIO_write(mem, "", 1); /* nul-terminate mem buffer */ BIO_get_mem_data(mem, &zCert); zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name); db_set(zHost, zCert, 1); free(zHost); zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name); db_set_int(zHost, trusted, 1); free(zHost); BIO_free(mem); } /* ** Get certificate for pUrlData->urlName from global config. ** Return NULL if no certificate found. */ X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){ char *zHost, *zCert; BIO *mem; X509 *cert; zHost = mprintf("cert:%s", pUrlData->useProxy ? pUrlData->hostname : pUrlData->name); zCert = db_get(zHost, NULL); free(zHost); if ( zCert==NULL ) return NULL; if ( pTrusted!=0 ){ zHost = mprintf("trusted:%s", pUrlData->useProxy ? pUrlData->hostname : pUrlData->name); *pTrusted = db_get_int(zHost, 0); free(zHost); } mem = BIO_new(BIO_s_mem()); BIO_puts(mem, zCert); cert = PEM_read_bio_X509(mem, NULL, 0, NULL); free(zCert); BIO_free(mem); return cert; } /* ** Send content out over the SSL connection. */ size_t ssl_send(void *NotUsed, void *pContent, size_t N){ |
︙ | ︙ |
Changes to src/http_transport.c.
︙ | ︙ | |||
50 51 52 53 54 55 56 | static int sshIn; /* From ssh subprocess to this process */ static FILE *sshOut; /* From this to ssh subprocess */ /* ** Return the current transport error message. */ | | | < < < < < < < < < < < < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < | | | | | | | > | | | | | | | > < < < < < < < < < < < < < < | < < < < < < > > | | | < < < < < < < < | | < | > | < < < | > | > | > > | | | | | | < < < | < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | < < < < < < < | | | | | | | | | | | | | | < < | | | | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | static int sshIn; /* From ssh subprocess to this process */ static FILE *sshOut; /* From this to ssh subprocess */ /* ** Return the current transport error message. */ const char *transport_errmsg(UrlData *pUrlData){ #ifdef FOSSIL_ENABLE_SSL if( pUrlData->isHttps ){ return ssl_errmsg(); } #endif return socket_errmsg(); } /* ** Retrieve send/receive counts from the transport layer. If "resetFlag" ** is true, then reset the counts. */ void transport_stats(i64 *pnSent, i64 *pnRcvd, int resetFlag){ if( pnSent ) *pnSent = transport.nSent; if( pnRcvd ) *pnRcvd = transport.nRcvd; if( resetFlag ){ transport.nSent = 0; transport.nRcvd = 0; } } /* ** Default SSH command */ #ifdef _WIN32 static char zDefaultSshCmd[] = "plink -ssh -T"; #else static char zDefaultSshCmd[] = "ssh -e none -T"; #endif /* ** SSH initialization of the transport layer */ int transport_ssh_open(UrlData *pUrlData){ /* For SSH we need to create and run SSH fossil http ** to talk to the remote machine. */ const char *zSsh; /* The base SSH command */ Blob zCmd; /* The SSH command */ char *zHost; /* The host name to contact */ int n; /* Size of prefix string */ socket_ssh_resolve_addr(pUrlData); zSsh = db_get("ssh-command", zDefaultSshCmd); blob_init(&zCmd, zSsh, -1); if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){ #ifdef _WIN32 blob_appendf(&zCmd, " -P %d", pUrlData->port); #else blob_appendf(&zCmd, " -p %d", pUrlData->port); #endif } if( g.fSshTrace ){ fossil_force_newline(); fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */ } if( pUrlData->user && pUrlData->user[0] ){ zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); }else{ zHost = mprintf("%s", pUrlData->name); } n = blob_size(&zCmd); blob_append(&zCmd, " ", 1); shell_escape(&zCmd, zHost); blob_append(&zCmd, " ", 1); shell_escape(&zCmd, mprintf("%s", pUrlData->fossil)); blob_append(&zCmd, " test-http", 10); if( pUrlData->path && pUrlData->path[0] ){ blob_append(&zCmd, " ", 1); shell_escape(&zCmd, mprintf("%s", pUrlData->path)); } if( g.fSshTrace ){ fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ } free(zHost); popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid); if( sshPid==0 ){ socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd); } blob_reset(&zCmd); return sshPid==0; } /* ** Open a connection to the server. The server is defined by the following ** variables: ** ** pUrlData->name Name of the server. Ex: www.fossil-scm.org ** pUrlData->port TCP/IP port. Ex: 80 ** pUrlData->isHttps Use TLS for the connection ** ** Return the number of errors. */ int transport_open(UrlData *pUrlData){ int rc = 0; if( transport.isOpen==0 ){ if( pUrlData->isSsh ){ rc = transport_ssh_open(pUrlData); if( rc==0 ) transport.isOpen = 1; }else if( pUrlData->isHttps ){ #ifdef FOSSIL_ENABLE_SSL rc = ssl_open(pUrlData); if( rc==0 ) transport.isOpen = 1; #else socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); rc = 1; #endif }else if( pUrlData->isFile ){ sqlite3_uint64 iRandId; sqlite3_randomness(sizeof(iRandId), &iRandId); transport.zOutFile = mprintf("%s-%llu-out.http", g.zRepositoryName, iRandId); transport.zInFile = mprintf("%s-%llu-in.http", g.zRepositoryName, iRandId); transport.pFile = fossil_fopen(transport.zOutFile, "wb"); if( transport.pFile==0 ){ fossil_fatal("cannot output temporary file: %s", transport.zOutFile); } transport.isOpen = 1; }else{ rc = socket_open(pUrlData); if( rc==0 ) transport.isOpen = 1; } } return rc; } /* ** Close the current connection */ void transport_close(UrlData *pUrlData){ if( transport.isOpen ){ free(transport.pBuf); transport.pBuf = 0; transport.nAlloc = 0; transport.nUsed = 0; transport.iCursor = 0; if( transport.pLog ){ fclose(transport.pLog); transport.pLog = 0; } if( pUrlData->isSsh ){ transport_ssh_close(); }else if( pUrlData->isHttps ){ #ifdef FOSSIL_ENABLE_SSL ssl_close(); #endif }else if( pUrlData->isFile ){ if( transport.pFile ){ fclose(transport.pFile); transport.pFile = 0; } file_delete(transport.zInFile); file_delete(transport.zOutFile); free(transport.zInFile); free(transport.zOutFile); }else{ socket_close(); } transport.isOpen = 0; } } /* ** Send content over the wire. */ void transport_send(UrlData *pUrlData, Blob *toSend){ char *z = blob_buffer(toSend); int n = blob_size(toSend); transport.nSent += n; if( pUrlData->isSsh ){ fwrite(z, 1, n, sshOut); fflush(sshOut); }else if( pUrlData->isHttps ){ #ifdef FOSSIL_ENABLE_SSL int sent; while( n>0 ){ sent = ssl_send(0, z, n); /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ if( sent<=0 ) break; n -= sent; } #endif }else if( pUrlData->isFile ){ fwrite(z, 1, n, transport.pFile); }else{ int sent; while( n>0 ){ sent = socket_send(0, z, n); /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ if( sent<=0 ) break; n -= sent; } } } /* ** This routine is called when the outbound message is complete and ** it is time to being receiving a reply. */ void transport_flip(UrlData *pUrlData){ if( pUrlData->isFile ){ char *zCmd; fclose(transport.pFile); zCmd = mprintf("\"%s\" http \"%s\" \"%s\" 127.0.0.1 \"%s\" --localauth", g.nameOfExe, transport.zOutFile, transport.zInFile, pUrlData->name ); fossil_system(zCmd); free(zCmd); transport.pFile = fossil_fopen(transport.zInFile, "rb"); } } |
︙ | ︙ | |||
427 428 429 430 431 432 433 | transport.pLog = pLog; } /* ** This routine is called when the inbound message has been received ** and it is time to start sending again. */ | | | | | | | | | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | transport.pLog = pLog; } /* ** This routine is called when the inbound message has been received ** and it is time to start sending again. */ void transport_rewind(UrlData *pUrlData){ if( pUrlData->isFile ){ transport_close(pUrlData); } } /* ** Read N bytes of content directly from the wire and write into ** the buffer. */ static int transport_fetch(UrlData *pUrlData, char *zBuf, int N){ int got; if( sshIn ){ int x; int wanted = N; got = 0; while( wanted>0 ){ x = read(sshIn, &zBuf[got], wanted); if( x<=0 ) break; got += x; wanted -= x; } }else if( pUrlData->isHttps ){ #ifdef FOSSIL_ENABLE_SSL got = ssl_receive(0, zBuf, N); #else got = 0; #endif }else if( pUrlData->isFile ){ got = fread(zBuf, 1, N, transport.pFile); }else{ got = socket_receive(0, zBuf, N); } /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ if( transport.pLog ){ fwrite(zBuf, 1, got, transport.pLog); fflush(transport.pLog); } return got; } /* ** Read N bytes of content from the wire and store in the supplied buffer. ** Return the number of bytes actually received. */ int transport_receive(UrlData *pUrlData, char *zBuf, int N){ int onHand; /* Bytes current held in the transport buffer */ int nByte = 0; /* Bytes of content received */ onHand = transport.nUsed - transport.iCursor; if( g.fSshTrace){ printf("Reading %d bytes with %d on hand... ", N, onHand); fflush(stdout); |
︙ | ︙ | |||
496 497 498 499 500 501 502 | transport.iCursor = 0; } N -= toMove; zBuf += toMove; nByte += toMove; } if( N>0 ){ | | | | 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | transport.iCursor = 0; } N -= toMove; zBuf += toMove; nByte += toMove; } if( N>0 ){ int got = transport_fetch(pUrlData, zBuf, N); if( got>0 ){ nByte += got; transport.nRcvd += got; } } if( g.fSshTrace ) printf("Got %d bytes\n", nByte); return nByte; } /* ** Load up to N new bytes of content into the transport.pBuf buffer. ** The buffer itself might be moved. And the transport.iCursor value ** might be reset to 0. */ static void transport_load_buffer(UrlData *pUrlData, int N){ int i, j; if( transport.nAlloc==0 ){ transport.nAlloc = N; transport.pBuf = fossil_malloc( N ); transport.iCursor = 0; transport.nUsed = 0; } |
︙ | ︙ | |||
533 534 535 536 537 538 539 | if( transport.nUsed + N > transport.nAlloc ){ char *pNew; transport.nAlloc = transport.nUsed + N; pNew = fossil_realloc(transport.pBuf, transport.nAlloc); transport.pBuf = pNew; } if( N>0 ){ | | | | | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | if( transport.nUsed + N > transport.nAlloc ){ char *pNew; transport.nAlloc = transport.nUsed + N; pNew = fossil_realloc(transport.pBuf, transport.nAlloc); transport.pBuf = pNew; } if( N>0 ){ i = transport_fetch(pUrlData, &transport.pBuf[transport.nUsed], N); if( i>0 ){ transport.nRcvd += i; transport.nUsed += i; } } } /* ** Fetch a single line of input where a line is all text up to the next ** \n character or until the end of input. Remove all trailing whitespace ** from the received line and zero-terminate the result. Return a pointer ** to the line. ** ** Each call to this routine potentially overwrites the returned buffer. */ char *transport_receive_line(UrlData *pUrlData){ int i; int iStart; i = iStart = transport.iCursor; while(1){ if( i >= transport.nUsed ){ transport_load_buffer(pUrlData, pUrlData->isSsh ? 2 : 1000); i -= iStart; iStart = 0; if( i >= transport.nUsed ){ transport.pBuf[i] = 0; transport.iCursor = i; break; } |
︙ | ︙ | |||
579 580 581 582 583 584 585 | } i++; } if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]); return &transport.pBuf[iStart]; } | > > > | | < < | < | > > > > > > > > > > > > | 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | } i++; } if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]); return &transport.pBuf[iStart]; } /* ** Global transport shutdown */ void transport_global_shutdown(UrlData *pUrlData){ if( pUrlData->isSsh ){ transport_ssh_close(); } if( pUrlData->isHttps ){ #ifdef FOSSIL_ENABLE_SSL ssl_global_shutdown(); #endif }else{ socket_global_shutdown(); } } /* ** Close SSH transport. */ void transport_ssh_close(void){ if( sshPid ){ /*printf("Closing SSH tunnel: ");*/ fflush(stdout); pclose2(sshIn, sshOut, sshPid); sshPid = 0; } } |
Changes to src/import.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@sqlite.org ** ******************************************************************************* ** | | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@sqlite.org ** ******************************************************************************* ** ** This file contains code used to import the content of a Git/SVN ** repository in the git-fast-import/svn-dump formats as a new Fossil ** repository. */ #include "config.h" #include "import.h" #include <assert.h> #if INTERFACE |
︙ | ︙ | |||
212 213 214 215 216 217 218 | return fossil_strcmp(pA->zName, pB->zName); } /* ** Compare two strings for sorting. */ static int string_cmp(const void *pLeft, const void *pRight){ | | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | return fossil_strcmp(pA->zName, pB->zName); } /* ** Compare two strings for sorting. */ static int string_cmp(const void *pLeft, const void *pRight){ const char *zLeft = *(const char **)pLeft; const char *zRight = *(const char **)pRight; return fossil_strcmp(zLeft, zRight); } /* Forward reference */ static void import_prior_files(void); /* |
︙ | ︙ | |||
441 442 443 444 445 446 447 | ** Do not search past the mx-th file. */ static ImportFile *import_find_file(const char *zName, int *pI, int mx){ int i = *pI; int nName = strlen(zName); while( i<mx ){ const char *z = gg.aFile[i].zName; | | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | ** Do not search past the mx-th file. */ static ImportFile *import_find_file(const char *zName, int *pI, int mx){ int i = *pI; int nName = strlen(zName); while( i<mx ){ const char *z = gg.aFile[i].zName; if( strncmp(zName, z, nName)==0 && (z[nName]==0 || z[nName]=='/') ){ *pI = i+1; return &gg.aFile[i]; } i++; } return 0; } |
︙ | ︙ | |||
486 487 488 489 490 491 492 | char *zFrom; char *zTo; char zLine[1000]; gg.xFinish = finish_noop; while( fgets(zLine, sizeof(zLine), pIn) ){ if( zLine[0]=='\n' || zLine[0]=='#' ) continue; | | | | 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | char *zFrom; char *zTo; char zLine[1000]; gg.xFinish = finish_noop; while( fgets(zLine, sizeof(zLine), pIn) ){ if( zLine[0]=='\n' || zLine[0]=='#' ) continue; if( strncmp(zLine, "blob", 4)==0 ){ gg.xFinish(); gg.xFinish = finish_blob; }else if( strncmp(zLine, "commit ", 7)==0 ){ gg.xFinish(); gg.xFinish = finish_commit; trim_newline(&zLine[7]); z = &zLine[7]; /* The argument to the "commit" line might match either of these ** patterns: |
︙ | ︙ | |||
514 515 516 517 518 519 520 | ** tag to the new commit. However, if there are multiple instances ** of pattern B with the same TAGNAME, then only put the tag on the ** last commit that holds that tag. ** ** None of the above is explained in the git-fast-export ** documentation. We had to figure it out via trial and error. */ | | | | | | | | | | | | | | | | | | | 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 | ** tag to the new commit. However, if there are multiple instances ** of pattern B with the same TAGNAME, then only put the tag on the ** last commit that holds that tag. ** ** None of the above is explained in the git-fast-export ** documentation. We had to figure it out via trial and error. */ for(i=5; i<strlen(z) && z[i]!='/'; i++){} gg.tagCommit = strncmp(&z[5], "tags", 4)==0; /* True for pattern B */ if( z[i+1]!=0 ) z += i+1; if( fossil_strcmp(z, "master")==0 ) z = "trunk"; gg.zBranch = fossil_strdup(z); gg.fromLoaded = 0; }else if( strncmp(zLine, "tag ", 4)==0 ){ gg.xFinish(); gg.xFinish = finish_tag; trim_newline(&zLine[4]); gg.zTag = fossil_strdup(&zLine[4]); }else if( strncmp(zLine, "reset ", 4)==0 ){ gg.xFinish(); }else if( strncmp(zLine, "checkpoint", 10)==0 ){ gg.xFinish(); }else if( strncmp(zLine, "feature", 7)==0 ){ gg.xFinish(); }else if( strncmp(zLine, "option", 6)==0 ){ gg.xFinish(); }else if( strncmp(zLine, "progress ", 9)==0 ){ gg.xFinish(); trim_newline(&zLine[9]); fossil_print("%s\n", &zLine[9]); fflush(stdout); }else if( strncmp(zLine, "data ", 5)==0 ){ fossil_free(gg.aData); gg.aData = 0; gg.nData = atoi(&zLine[5]); if( gg.nData ){ int got; gg.aData = fossil_malloc( gg.nData+1 ); got = fread(gg.aData, 1, gg.nData, pIn); if( got!=gg.nData ){ fossil_fatal("short read: got %d of %d bytes", got, gg.nData); } gg.aData[got] = 0; if( gg.zComment==0 && gg.xFinish==finish_commit ){ gg.zComment = gg.aData; gg.aData = 0; gg.nData = 0; } } }else if( strncmp(zLine, "author ", 7)==0 ){ /* No-op */ }else if( strncmp(zLine, "mark ", 5)==0 ){ trim_newline(&zLine[5]); fossil_free(gg.zMark); gg.zMark = fossil_strdup(&zLine[5]); }else if( strncmp(zLine, "tagger ", 7)==0 || strncmp(zLine, "committer ",10)==0 ){ sqlite3_int64 secSince1970; for(i=0; zLine[i] && zLine[i]!='<'; i++){} if( zLine[i]==0 ) goto malformed_line; z = &zLine[i+1]; for(i=i+1; zLine[i] && zLine[i]!='>'; i++){} if( zLine[i]==0 ) goto malformed_line; zLine[i] = 0; fossil_free(gg.zUser); gg.zUser = fossil_strdup(z); secSince1970 = 0; for(i=i+2; fossil_isdigit(zLine[i]); i++){ secSince1970 = secSince1970*10 + zLine[i] - '0'; } fossil_free(gg.zDate); gg.zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", secSince1970); gg.zDate[10] = 'T'; }else if( strncmp(zLine, "from ", 5)==0 ){ trim_newline(&zLine[5]); fossil_free(gg.zFromMark); gg.zFromMark = fossil_strdup(&zLine[5]); fossil_free(gg.zFrom); gg.zFrom = resolve_committish(&zLine[5]); }else if( strncmp(zLine, "merge ", 6)==0 ){ trim_newline(&zLine[6]); if( gg.nMerge>=gg.nMergeAlloc ){ gg.nMergeAlloc = gg.nMergeAlloc*2 + 10; gg.azMerge = fossil_realloc(gg.azMerge, gg.nMergeAlloc*sizeof(char*)); } gg.azMerge[gg.nMerge] = resolve_committish(&zLine[6]); if( gg.azMerge[gg.nMerge] ) gg.nMerge++; }else if( strncmp(zLine, "M ", 2)==0 ){ import_prior_files(); z = &zLine[2]; zPerm = next_token(&z); zUuid = next_token(&z); zName = rest_of_line(&z); dequote_git_filename(zName); i = 0; pFile = import_find_file(zName, &i, gg.nFile); if( pFile==0 ){ pFile = import_add_file(); pFile->zName = fossil_strdup(zName); } pFile->isExe = (fossil_strcmp(zPerm, "100755")==0); pFile->isLink = (fossil_strcmp(zPerm, "120000")==0); fossil_free(pFile->zUuid); pFile->zUuid = resolve_committish(zUuid); pFile->isFrom = 0; }else if( strncmp(zLine, "D ", 2)==0 ){ import_prior_files(); z = &zLine[2]; zName = rest_of_line(&z); dequote_git_filename(zName); i = 0; while( (pFile = import_find_file(zName, &i, gg.nFile))!=0 ){ if( pFile->isFrom==0 ) continue; fossil_free(pFile->zName); fossil_free(pFile->zPrior); fossil_free(pFile->zUuid); *pFile = gg.aFile[--gg.nFile]; i--; } }else if( strncmp(zLine, "C ", 2)==0 ){ int nFrom; import_prior_files(); z = &zLine[2]; zFrom = next_token(&z); zTo = rest_of_line(&z); i = 0; mx = gg.nFile; |
︙ | ︙ | |||
663 664 665 666 667 668 669 | } pNew->isExe = pFile->isExe; pNew->isLink = pFile->isLink; pNew->zUuid = fossil_strdup(pFile->zUuid); pNew->isFrom = 0; } }else | | | 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | } pNew->isExe = pFile->isExe; pNew->isLink = pFile->isLink; pNew->zUuid = fossil_strdup(pFile->zUuid); pNew->isFrom = 0; } }else if( strncmp(zLine, "R ", 2)==0 ){ int nFrom; import_prior_files(); z = &zLine[2]; zFrom = next_token(&z); zTo = rest_of_line(&z); i = 0; nFrom = strlen(zFrom); |
︙ | ︙ | |||
691 692 693 694 695 696 697 | pNew->isFrom = 0; gg.nFile--; *pFile = *pNew; memset(pNew, 0, sizeof(*pNew)); } fossil_fatal("cannot handle R records, use --full-tree"); }else | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | > > | < | > > > > > > > > > > > > > > > < < | > > > > > > > > > > > > > > > > > > | > > | | < < < < < < < < < < < < < < < < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | > > > | 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 | pNew->isFrom = 0; gg.nFile--; *pFile = *pNew; memset(pNew, 0, sizeof(*pNew)); } fossil_fatal("cannot handle R records, use --full-tree"); }else if( strncmp(zLine, "deleteall", 9)==0 ){ gg.fromLoaded = 1; }else if( strncmp(zLine, "N ", 2)==0 ){ /* No-op */ }else { goto malformed_line; } } gg.xFinish(); if( gg.hasLinks ){ db_set_int("allow-symlinks", 1, 0); } import_reset(1); return; malformed_line: trim_newline(zLine); fossil_fatal("bad fast-import line: [%s]", zLine); return; } static struct{ int rev; /* SVN revision number */ char *zDate; /* Date/time stamp */ char *zUser; /* User name */ char *zComment; /* Comment of a commit */ const char *zTrunk; /* Name of trunk folder in repo root */ int lenTrunk; /* String length of zTrunk */ const char *zBranches; /* Name of branches folder in repo root */ int lenBranches; /* String length of zBranches */ const char *zTags; /* Name of tags folder in repo root */ int lenTags; /* String length of zTags */ Bag newBranches; /* Branches that were created in this revision */ int incrFlag; /* Add svn-rev-nn tags on every checkin */ } gsvn; typedef struct { char *zKey; char *zVal; } KeyVal; typedef struct { KeyVal *aHeaders; int nHeaders; char *pRawProps; KeyVal *aProps; int nProps; Blob content; int contentFlag; } SvnRecord; #define svn_find_header(rec, zHeader) \ svn_find_keyval((rec).aHeaders, (rec).nHeaders, (zHeader)) #define svn_find_prop(rec, zProp) \ svn_find_keyval((rec).aProps, (rec).nProps, (zProp)) static char *svn_find_keyval( KeyVal *aKeyVal, int nKeyVal, const char *zKey ){ int i; for(i=0; i<nKeyVal; i++){ if( fossil_strcmp(aKeyVal[i].zKey, zKey)==0 ){ return aKeyVal[i].zVal; } } return 0; } static void svn_free_rec(SvnRecord *rec){ int i; for(i=0; i<rec->nHeaders; i++){ fossil_free(rec->aHeaders[i].zKey); } fossil_free(rec->aHeaders); fossil_free(rec->aProps); fossil_free(rec->pRawProps); blob_reset(&rec->content); } static int svn_read_headers(FILE *pIn, SvnRecord *rec){ char zLine[1000]; rec->aHeaders = 0; rec->nHeaders = 0; while( fgets(zLine, sizeof(zLine), pIn) ){ if( zLine[0]!='\n' ) break; } if( feof(pIn) ) return 0; do{ char *sep; if( zLine[0]=='\n' ) break; rec->nHeaders += 1; rec->aHeaders = fossil_realloc(rec->aHeaders, sizeof(rec->aHeaders[0])*rec->nHeaders); rec->aHeaders[rec->nHeaders-1].zKey = mprintf("%s", zLine); sep = strchr(rec->aHeaders[rec->nHeaders-1].zKey, ':'); if( !sep ){ trim_newline(zLine); fossil_fatal("bad header line: [%s]", zLine); } *sep = 0; rec->aHeaders[rec->nHeaders-1].zVal = sep+1; sep = strchr(rec->aHeaders[rec->nHeaders-1].zVal, '\n'); *sep = 0; while(rec->aHeaders[rec->nHeaders-1].zVal && fossil_isspace(*(rec->aHeaders[rec->nHeaders-1].zVal)) ) { rec->aHeaders[rec->nHeaders-1].zVal++; } }while( fgets(zLine, sizeof(zLine), pIn) ); if( zLine[0]!='\n' ){ trim_newline(zLine); fossil_fatal("svn-dump data ended unexpectedly"); } return 1; } static void svn_read_props(FILE *pIn, SvnRecord *rec){ int nRawProps = 0; char *pRawProps; const char *zLen; rec->pRawProps = 0; rec->aProps = 0; rec->nProps = 0; zLen = svn_find_header(*rec, "Prop-content-length"); if( zLen ){ nRawProps = atoi(zLen); } if( nRawProps ){ int got; char *zLine; rec->pRawProps = pRawProps = fossil_malloc( nRawProps ); got = fread(rec->pRawProps, 1, nRawProps, pIn); if( got!=nRawProps ){ fossil_fatal("short read: got %d of %d bytes", got, nRawProps); } if( memcmp(&pRawProps[got-10], "PROPS-END\n", 10)!=0 ){ fossil_fatal("svn-dump data ended unexpectedly"); } zLine = pRawProps; while( zLine<(pRawProps+nRawProps-10) ){ char *eol; int propLen; if( zLine[0]=='D' ){ propLen = atoi(&zLine[2]); eol = strchr(zLine, '\n'); zLine = eol+1+propLen+1; }else{ if( zLine[0]!='K' ){ fossil_fatal("svn-dump data format broken"); } propLen = atoi(&zLine[2]); eol = strchr(zLine, '\n'); zLine = eol+1; eol = zLine+propLen; if( *eol!='\n' ){ fossil_fatal("svn-dump data format broken"); } *eol = 0; rec->nProps += 1; rec->aProps = fossil_realloc(rec->aProps, sizeof(rec->aProps[0])*rec->nProps); rec->aProps[rec->nProps-1].zKey = zLine; zLine = eol+1; if( zLine[0]!='V' ){ fossil_fatal("svn-dump data format broken"); } propLen = atoi(&zLine[2]); eol = strchr(zLine, '\n'); zLine = eol+1; eol = zLine+propLen; if( *eol!='\n' ){ fossil_fatal("svn-dump data format broken"); } *eol = 0; rec->aProps[rec->nProps-1].zVal = zLine; zLine = eol+1; } } } } static int svn_read_rec(FILE *pIn, SvnRecord *rec){ const char *zLen; int nLen = 0; if( svn_read_headers(pIn, rec)==0 ) return 0; svn_read_props(pIn, rec); blob_zero(&rec->content); zLen = svn_find_header(*rec, "Text-content-length"); if( zLen ){ rec->contentFlag = 1; nLen = atoi(zLen); blob_read_from_channel(&rec->content, pIn, nLen); if( blob_size(&rec->content)!=nLen ){ fossil_fatal("short read: got %d of %d bytes", blob_size(&rec->content), nLen ); } }else{ rec->contentFlag = 0; } return 1; } /* ** Returns the UUID for the RID, or NULL if not found. ** The returned string is allocated via db_text() and must be ** free()d by the caller. */ char * rid_to_uuid(int rid) { return db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); } #define SVN_UNKNOWN 0 #define SVN_TRUNK 1 #define SVN_BRANCH 2 #define SVN_TAG 3 #define MAX_INT_32 (0x7FFFFFFFL) static void svn_finish_revision(){ Blob manifest; static Stmt getChanges; static Stmt getFiles; static Stmt setRid; Blob mcksum; blob_zero(&manifest); db_static_prepare(&getChanges, "SELECT tid, tname, ttype, tparent" " FROM xrevisions, xbranches ON (tbranch=tid)" " WHERE trid ISNULL"); db_static_prepare(&getFiles, "SELECT tpath, tuuid, tperm FROM xfiles" " WHERE tbranch=:branch ORDER BY tpath"); db_prepare(&setRid, "UPDATE xrevisions SET trid=:rid" " WHERE trev=%d AND tbranch=:branch", gsvn.rev); while( db_step(&getChanges)==SQLITE_ROW ){ int branchId = db_column_int(&getChanges, 0); const char *zBranch = db_column_text(&getChanges, 1); int branchType = db_column_int(&getChanges, 2); int parentRid = db_column_int(&getChanges, 3); int mergeRid = parentRid; Manifest *pParentManifest = 0; ManifestFile *pParentFile = 0; int sameAsParent = 1; int parentBranch = 0; if( !bag_find(&gsvn.newBranches, branchId) ){ parentRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions" " WHERE trev<%d AND tbranch=%d", gsvn.rev, branchId); } if( parentRid>0 ){ pParentManifest = manifest_get(parentRid, CFTYPE_MANIFEST, 0); pParentFile = manifest_file_next(pParentManifest, 0); parentBranch = db_int(0, "SELECT tbranch FROM xrevisions WHERE trid=%d", parentRid); if( parentBranch!=branchId && branchType!=SVN_TAG ){ sameAsParent = 0; } } if( mergeRid<MAX_INT_32 ){ if( gsvn.zComment ){ blob_appendf(&manifest, "C %F\n", gsvn.zComment); }else{ blob_append(&manifest, "C (no\\scomment)\n", 16); } blob_appendf(&manifest, "D %s\n", gsvn.zDate); db_bind_int(&getFiles, ":branch", branchId); while( db_step(&getFiles)==SQLITE_ROW ){ const char *zFile = db_column_text(&getFiles, 0); const char *zUuid = db_column_text(&getFiles, 1); const char *zPerm = db_column_text(&getFiles, 2); if( zPerm ){ blob_appendf(&manifest, "F %F %s %s\n", zFile, zUuid, zPerm); }else{ blob_appendf(&manifest, "F %F %s\n", zFile, zUuid); } if( sameAsParent ){ if( !pParentFile || fossil_strcmp(pParentFile->zName,zFile)!=0 || fossil_strcmp(pParentFile->zUuid,zUuid)!=0 || fossil_strcmp(pParentFile->zPerm,zPerm)!=0 ){ sameAsParent = 0; }else{ pParentFile = manifest_file_next(pParentManifest, 0); } } } if( pParentFile ){ sameAsParent = 0; } db_reset(&getFiles); if( !sameAsParent ){ if( parentRid>0 ){ char *zParentUuid = rid_to_uuid(parentRid); if( parentRid==mergeRid || mergeRid==0){ char *zParentBranch = db_text(0, "SELECT tname FROM xbranches WHERE tid=%d", parentBranch ); blob_appendf(&manifest, "P %s\n", zParentUuid); blob_appendf(&manifest, "T *branch * %F\n", zBranch); blob_appendf(&manifest, "T *sym-%F *\n", zBranch); if( gsvn.incrFlag ){ blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); } blob_appendf(&manifest, "T -sym-%F *\n", zParentBranch); fossil_free(zParentBranch); }else{ char *zMergeUuid = rid_to_uuid(mergeRid); blob_appendf(&manifest, "P %s %s\n", zParentUuid, zMergeUuid); if( gsvn.incrFlag ){ blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); } fossil_free(zMergeUuid); } fossil_free(zParentUuid); }else{ blob_appendf(&manifest, "T *branch * %F\n", zBranch); blob_appendf(&manifest, "T *sym-%F *\n", zBranch); if( gsvn.incrFlag ){ blob_appendf(&manifest, "T +sym-svn-rev-%d *\n", gsvn.rev); } } }else if( branchType==SVN_TAG ){ char *zParentUuid = rid_to_uuid(parentRid); blob_reset(&manifest); blob_appendf(&manifest, "D %s\n", gsvn.zDate); blob_appendf(&manifest, "T +sym-%F %s\n", zBranch, zParentUuid); fossil_free(zParentUuid); } }else{ char *zParentUuid = rid_to_uuid(parentRid); blob_appendf(&manifest, "D %s\n", gsvn.zDate); if( branchType!=SVN_TAG ){ blob_appendf(&manifest, "T +closed %s\n", zParentUuid); }else{ blob_appendf(&manifest, "T -sym-%F %s\n", zBranch, zParentUuid); } fossil_free(zParentUuid); } if( gsvn.zUser ){ blob_appendf(&manifest, "U %F\n", gsvn.zUser); }else{ const char *zUserOvrd = find_option("user-override",0,1); blob_appendf(&manifest, "U %F\n", zUserOvrd ? zUserOvrd : login_name()); } md5sum_blob(&manifest, &mcksum); blob_appendf(&manifest, "Z %b\n", &mcksum); blob_reset(&mcksum); if( !sameAsParent ){ int rid = content_put(&manifest); db_bind_int(&setRid, ":branch", branchId); db_bind_int(&setRid, ":rid", rid); db_step(&setRid); db_reset(&setRid); }else if( branchType==SVN_TAG ){ content_put(&manifest); db_bind_int(&setRid, ":branch", branchId); db_bind_int(&setRid, ":rid", parentRid); db_step(&setRid); db_reset(&setRid); }else if( mergeRid==MAX_INT_32 ){ content_put(&manifest); db_multi_exec("DELETE FROM xrevisions WHERE tbranch=%d AND trev=%d", branchId, gsvn.rev); }else{ db_multi_exec("DELETE FROM xrevisions WHERE tbranch=%d AND trev=%d", branchId, gsvn.rev); } blob_reset(&manifest); manifest_destroy(pParentManifest); } db_reset(&getChanges); db_finalize(&setRid); } static u64 svn_get_varint(const char **pz){ unsigned int v = 0; do{ v = (v<<7) | ((*pz)[0]&0x7f); }while( (*pz)++[0]&0x80 ); return v; } static void svn_apply_svndiff(Blob *pDiff, Blob *pSrc, Blob *pOut){ const char *zDiff = blob_buffer(pDiff); char *zOut; if( blob_size(pDiff)<4 || memcmp(zDiff, "SVN", 4)!=0 ){ fossil_fatal("Invalid svndiff0 format"); } zDiff += 4; blob_zero(pOut); while( zDiff<(blob_buffer(pDiff)+blob_size(pDiff)) ){ u64 lenOut, lenInst, lenData, lenOld; const char *zInst; const char *zData; u64 offSrc = svn_get_varint(&zDiff); /*lenSrc =*/ svn_get_varint(&zDiff); lenOut = svn_get_varint(&zDiff); lenInst = svn_get_varint(&zDiff); lenData = svn_get_varint(&zDiff); zInst = zDiff; zData = zInst+lenInst; lenOld = blob_size(pOut); blob_resize(pOut, lenOut+lenOld); zOut = blob_buffer(pOut)+lenOld; while( zDiff<zInst+lenInst ){ u64 lenCpy = (*zDiff)&0x3f; const char *zCpy; switch( (*zDiff)&0xC0 ){ case 0x00: zCpy = blob_buffer(pSrc)+offSrc; break; case 0x40: zCpy = blob_buffer(pOut); break; case 0x80: zCpy = zData; break; default: fossil_fatal("Invalid svndiff0 instruction"); } zDiff++; if( lenCpy==0 ){ lenCpy = svn_get_varint(&zDiff); } if( zCpy!=zData ){ zCpy += svn_get_varint(&zDiff); }else{ zData += lenCpy; } while( lenCpy-- > 0 ){ *zOut++ = *zCpy++; } } zDiff += lenData; } } /* ** Extract the branch or tag that the given path is on. Return the branch ID. */ static int svn_parse_path(char *zPath, char **zFile, int *type){ char *zBranch = 0; int branchId = 0; *type = SVN_UNKNOWN; *zFile = 0; if( gsvn.lenTrunk==0 ){ zBranch = "trunk"; *zFile = zPath; *type = SVN_TRUNK; }else if( strncmp(zPath, gsvn.zTrunk, gsvn.lenTrunk-1)==0 ){ if( zPath[gsvn.lenTrunk-1]=='/' || zPath[gsvn.lenTrunk-1]==0 ){ zBranch = "trunk"; *zFile = zPath+gsvn.lenTrunk; *type = SVN_TRUNK; }else{ zBranch = 0; *type = SVN_UNKNOWN; } }else{ if( strncmp(zPath, gsvn.zBranches, gsvn.lenBranches)==0 ){ *zFile = zBranch = zPath+gsvn.lenBranches; *type = SVN_BRANCH; }else if( strncmp(zPath, gsvn.zTags, gsvn.lenTags)==0 ){ *zFile = zBranch = zPath+gsvn.lenTags; *type = SVN_TAG; }else{ /* Not a branch, tag or trunk */ return 0; } while( **zFile && **zFile!='/' ){ (*zFile)++; } if( **zFile ){ **zFile = '\0'; (*zFile)++; } } if( *type!=SVN_UNKNOWN ){ branchId = db_int(0, "SELECT tid FROM xbranches WHERE tname=%Q AND ttype=%d", zBranch, *type); if( branchId==0 ){ db_multi_exec("INSERT INTO xbranches (tname, ttype) VALUES(%Q, %d)", zBranch, *type); branchId = db_last_insert_rowid(); } } return branchId; } /* ** Read the svn-dump format from pIn and insert the corresponding ** content into the database. */ static void svn_dump_import(FILE *pIn){ SvnRecord rec; int ver; char *zTemp; const char *zUuid; Stmt addFile; Stmt delPath; Stmt addRev; Stmt cpyPath; Stmt cpyRoot; Stmt revSrc; /* version */ if( svn_read_rec(pIn, &rec) && (zTemp = svn_find_header(rec, "SVN-fs-dump-format-version")) ){ ver = atoi(zTemp); if( ver!=2 && ver!=3 ){ fossil_fatal("Unknown svn-dump format version: %d", ver); } }else{ fossil_fatal("Input is not an svn-dump!"); } svn_free_rec(&rec); /* UUID */ if( !svn_read_rec(pIn, &rec) || !(zUuid = svn_find_header(rec, "UUID")) ){ /* Removed the following line since UUID is not actually used fossil_fatal("Missing UUID!"); */ } svn_free_rec(&rec); /* content */ db_prepare(&addFile, "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" " VALUES(:path, :branch, (SELECT uuid FROM blob WHERE rid=:rid), :perm)" ); db_prepare(&delPath, "DELETE FROM xfiles" " WHERE (tpath=:path OR (tpath>:path||'/' AND tpath<:path||'0'))" " AND tbranch=:branch" ); db_prepare(&addRev, "INSERT OR IGNORE INTO xrevisions (trev, tbranch) VALUES(:rev, :branch)" ); db_prepare(&cpyPath, "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" " SELECT :path||:sep||substr(filename, length(:srcpath)+2), :branch, uuid, perm" " FROM xfoci" " WHERE checkinID=:rid" " AND filename>:srcpath||'/'" " AND filename<:srcpath||'0'" ); db_prepare(&cpyRoot, "INSERT INTO xfiles (tpath, tbranch, tuuid, tperm)" " SELECT :path||:sep||filename, :branch, uuid, perm" " FROM xfoci" " WHERE checkinID=:rid" ); db_prepare(&revSrc, "UPDATE xrevisions SET tparent=:parent" " WHERE trev=:rev AND tbranch=:branch AND tparent<:parent" ); gsvn.rev = -1; bag_init(&gsvn.newBranches); while( svn_read_rec(pIn, &rec) ){ if( (zTemp = svn_find_header(rec, "Revision-number")) ){ /* revision node */ /* finish previous revision */ char *zDate = NULL; if( gsvn.rev>=0 ){ svn_finish_revision(); fossil_free(gsvn.zUser); fossil_free(gsvn.zComment); fossil_free(gsvn.zDate); bag_clear(&gsvn.newBranches); } /* start new revision */ gsvn.rev = atoi(zTemp); gsvn.zUser = mprintf("%s", svn_find_prop(rec, "svn:author")); gsvn.zComment = mprintf("%s", svn_find_prop(rec, "svn:log")); zDate = svn_find_prop(rec, "svn:date"); if( zDate ){ gsvn.zDate = date_in_standard_format(zDate); }else{ gsvn.zDate = date_in_standard_format("now"); } db_bind_int(&addRev, ":rev", gsvn.rev); fossil_print("\rImporting SVN revision: %d", gsvn.rev); }else if( (zTemp = svn_find_header(rec, "Node-path")) ){ /* file/dir node */ char *zFile; int branchType; int branchId = svn_parse_path(zTemp, &zFile, &branchType); char *zAction = svn_find_header(rec, "Node-action"); char *zKind = svn_find_header(rec, "Node-kind"); char *zPerm = svn_find_prop(rec, "svn:executable") ? "x" : 0; int deltaFlag = 0; int srcRev = 0; if( branchId==0 ){ svn_free_rec(&rec); continue; } if( (zTemp = svn_find_header(rec, "Text-delta")) ){ deltaFlag = strncmp(zTemp, "true", 4)==0; } if( strncmp(zAction, "delete", 6)==0 || strncmp(zAction, "replace", 7)==0 ) { db_bind_int(&addRev, ":branch", branchId); db_step(&addRev); db_reset(&addRev); if( zFile[0]!=0 ){ db_bind_text(&delPath, ":path", zFile); db_bind_int(&delPath, ":branch", branchId); db_step(&delPath); db_reset(&delPath); }else{ db_multi_exec("DELETE FROM xfiles WHERE tbranch=%d", branchId); db_bind_int(&revSrc, ":parent", MAX_INT_32); db_bind_int(&revSrc, ":rev", gsvn.rev); db_bind_int(&revSrc, ":branch", branchId); db_step(&revSrc); db_reset(&revSrc); } } /* no 'else' here since 'replace' does both a 'delete' and an 'add' */ if( strncmp(zAction, "add", 3)==0 || strncmp(zAction, "replace", 7)==0 ) { char *zSrcPath = svn_find_header(rec, "Node-copyfrom-path"); char *zSrcFile; int srcRid = 0; if( zSrcPath ){ int srcBranch; zTemp = svn_find_header(rec, "Node-copyfrom-rev"); if( zTemp ){ srcRev = atoi(zTemp); }else{ fossil_fatal("Missing copyfrom-rev"); } srcBranch = svn_parse_path(zSrcPath, &zSrcFile, &branchType); if( srcBranch==0 ){ fossil_fatal("Copy from path outside the import paths"); } srcRid = db_int(0, "SELECT trid, max(trev) FROM xrevisions" " WHERE trev<=%d AND tbranch=%d", srcRev, srcBranch); if( srcRid>0 && srcBranch!=branchId ){ db_bind_int(&addRev, ":branch", branchId); db_step(&addRev); db_reset(&addRev); db_bind_int(&revSrc, ":parent", srcRid); db_bind_int(&revSrc, ":rev", gsvn.rev); db_bind_int(&revSrc, ":branch", branchId); db_step(&revSrc); db_reset(&revSrc); } } if( zKind==0 ){ fossil_fatal("Missing Node-kind"); }else if( strncmp(zKind, "dir", 3)==0 ){ if( zSrcPath ){ if( srcRid>0 ){ if( zSrcFile[0]==0 ){ db_bind_text(&cpyRoot, ":path", zFile); if( zFile[0]!=0 ){ db_bind_text(&cpyRoot, ":sep", "/"); }else{ db_bind_text(&cpyRoot, ":sep", ""); } db_bind_int(&cpyRoot, ":branch", branchId); db_bind_int(&cpyRoot, ":rid", srcRid); db_step(&cpyRoot); db_reset(&cpyRoot); }else{ db_bind_text(&cpyPath, ":path", zFile); if( zFile[0]!=0 ){ db_bind_text(&cpyPath, ":sep", "/"); }else{ db_bind_text(&cpyPath, ":sep", ""); } db_bind_int(&cpyPath, ":branch", branchId); db_bind_text(&cpyPath, ":srcpath", zSrcFile); db_bind_int(&cpyPath, ":rid", srcRid); db_step(&cpyPath); db_reset(&cpyPath); } } } if( zFile[0]==0 ){ bag_insert(&gsvn.newBranches, branchId); } }else{ int rid = 0; if( zSrcPath ){ rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" " SELECT uuid FROM xfoci" " WHERE checkinID=%d AND filename=%Q" ")", srcRid, zSrcFile); } if( deltaFlag ){ Blob deltaSrc; Blob target; if( rid!=0 ){ content_get(rid, &deltaSrc); }else{ blob_zero(&deltaSrc); } svn_apply_svndiff(&rec.content, &deltaSrc, &target); rid = content_put(&target); }else if( rec.contentFlag ){ rid = content_put(&rec.content); } db_bind_text(&addFile, ":path", zFile); db_bind_int(&addFile, ":branch", branchId); db_bind_int(&addFile, ":rid", rid); db_bind_text(&addFile, ":perm", zPerm); db_step(&addFile); db_reset(&addFile); db_bind_int(&addRev, ":branch", branchId); db_step(&addRev); db_reset(&addRev); } }else if( strncmp(zAction, "change", 6)==0 ){ int rid = 0; if( zKind==0 ){ fossil_fatal("Missing Node-kind"); } if( strncmp(zKind, "dir", 3)!=0 ){ if( deltaFlag ){ Blob deltaSrc; Blob target; rid = db_int(0, "SELECT rid FROM blob WHERE uuid=(" " SELECT uuid FROM xfiles" " WHERE tpath=%Q AND tbranch=%d" ")", zFile, branchId); content_get(rid, &deltaSrc); svn_apply_svndiff(&rec.content, &deltaSrc, &target); rid = content_put(&target); }else{ rid = content_put(&rec.content); } db_bind_text(&addFile, ":path", zFile); db_bind_int(&addFile, ":branch", branchId); db_bind_int(&addFile, ":rid", rid); db_bind_text(&addFile, ":perm", zPerm); db_step(&addFile); db_reset(&addFile); db_bind_int(&addRev, ":branch", branchId); db_step(&addRev); db_reset(&addRev); } }else if( strncmp(zAction, "delete", 6)!=0 ){ /* already did this one above */ fossil_fatal("Unknown Node-action"); } }else{ fossil_fatal("Unknown record type"); } svn_free_rec(&rec); } svn_finish_revision(); fossil_free(gsvn.zUser); fossil_free(gsvn.zComment); fossil_free(gsvn.zDate); db_finalize(&addFile); db_finalize(&delPath); db_finalize(&addRev); db_finalize(&cpyPath); db_finalize(&cpyRoot); db_finalize(&revSrc); fossil_print(" Done!\n"); } /* ** COMMAND: import ** ** Usage: %fossil import ?--git? ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? ** or: %fossil import --svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE? ** ** Read interchange format generated by another VCS and use it to ** construct a new Fossil repository named by the NEW-REPOSITORY ** argument. If no input file is supplied the interchange format ** data is read from standard input. ** ** The following formats are currently understood by this command ** ** --git Import from the git-fast-export file format (default) ** ** --svn Import from the svnadmin-dump file format. The default ** behaviour (unless overridden by --flat) is to treat 3 ** folders in the SVN root as special, following the ** common layout of SVN repositories. These are (by ** default) trunk/, branches/ and tags/ ** Options: ** --trunk FOLDER Name of trunk folder ** --branches FOLDER Name of branches folder ** --tags FOLDER Name of tags folder ** --base PATH Path to project root in repository ** --flat The whole dump is a single branch ** ** Common Options: ** -i|--incremental allow importing into an existing repository ** -f|--force overwrite repository if already exist ** ** The --incremental option allows an existing repository to be extended ** with new content. ** ** ** See also: export */ void import_cmd(void){ char *zPassword; FILE *pIn; Stmt q; int forceFlag = find_option("force", "f", 0)!=0; int svnFlag = find_option("svn", 0, 0)!=0; /* Options common to all input formats */ int incrFlag = find_option("incremental", "i", 0)!=0; /* Options for --svn only */ const char *zBase=""; int flatFlag=0; if( svnFlag ){ /* Get --svn related options here, so verify_all_options() fail when svn * only option are specify with --git */ zBase = find_option("base", 0, 1); flatFlag = find_option("flat", 0, 0)!=0; gsvn.zTrunk = find_option("trunk", 0, 1); gsvn.zBranches = find_option("branches", 0, 1); gsvn.zTags = find_option("tags", 0, 1); gsvn.incrFlag = incrFlag; }else{ find_option("git",0,0); /* Skip the --git option for now */ } verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("--git|--svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE?"); } if( g.argc==4 ){ pIn = fossil_fopen(g.argv[3], "rb"); }else{ pIn = stdin; fossil_binary_mode(pIn); } if( !incrFlag ){ if( forceFlag ) file_delete(g.argv[2]); db_create_repository(g.argv[2]); } db_open_repository(g.argv[2]); db_open_config(0); db_begin_transaction(); if( !incrFlag ) db_initial_setup(0, 0, 0); if( svnFlag ){ db_multi_exec( "CREATE TEMP TABLE xrevisions(" " trev INTEGER, tbranch INT, trid INT, tparent INT DEFAULT 0," " UNIQUE(tbranch, trev)" ");" "CREATE INDEX temp.i_xrevisions ON xrevisions(trid);" "CREATE TEMP TABLE xfiles(" " tpath TEXT, tbranch INT, tuuid TEXT, tperm TEXT," " UNIQUE (tbranch, tpath) ON CONFLICT REPLACE" ");" "CREATE TEMP TABLE xbranches(" " tid INTEGER PRIMARY KEY, tname TEXT, ttype INT," " UNIQUE(tname, ttype)" ");" "CREATE VIRTUAL TABLE temp.xfoci USING files_of_checkin;" ); if( zBase==0 ){ zBase = ""; } if( strlen(zBase)>0 ){ if( zBase[strlen(zBase)-1]!='/' ){ zBase = mprintf("%s/", zBase); } } if( flatFlag ){ gsvn.zTrunk = zBase; gsvn.zBranches = 0; gsvn.zTags = 0; gsvn.lenTrunk = strlen(zBase); gsvn.lenBranches = 0; gsvn.lenTags = 0; }else{ if( gsvn.zTrunk==0 ){ gsvn.zTrunk = "trunk/"; } if( gsvn.zBranches==0 ){ gsvn.zBranches = "branches/"; } if( gsvn.zTags==0 ){ gsvn.zTags = "tags/"; } gsvn.zTrunk = mprintf("%s%s", zBase, gsvn.zTrunk); gsvn.zBranches = mprintf("%s%s", zBase, gsvn.zBranches); gsvn.zTags = mprintf("%s%s", zBase, gsvn.zTags); gsvn.lenTrunk = strlen(gsvn.zTrunk); gsvn.lenBranches = strlen(gsvn.zBranches); gsvn.lenTags = strlen(gsvn.zTags); if( gsvn.zTrunk[gsvn.lenTrunk-1]!='/' ){ gsvn.zTrunk = mprintf("%s/", gsvn.zTrunk); gsvn.lenTrunk++; } if( gsvn.zBranches[gsvn.lenBranches-1]!='/' ){ gsvn.zBranches = mprintf("%s/", gsvn.zBranches); gsvn.lenBranches++; } if( gsvn.zTags[gsvn.lenTags-1]!='/' ){ gsvn.zTags = mprintf("%s/", gsvn.zTags); gsvn.lenTags++; } } svn_dump_import(pIn); }else{ /* The following temp-tables are used to hold information needed for ** the import. ** ** The XMARK table provides a mapping from fast-import "marks" and symbols ** into artifact ids (UUIDs - the 40-byte hex SHA1 hash of artifacts). ** Given any valid fast-import symbol, the corresponding fossil rid and ** uuid can found by searching against the xmark.tname field. ** ** The XBRANCH table maps commit marks and symbols into the branch those ** commits belong to. If xbranch.tname is a fast-import symbol for a ** check-in then xbranch.brnm is the branch that check-in is part of. ** ** The XTAG table records information about tags that need to be applied ** to various branches after the import finishes. The xtag.tcontent field ** contains the text of an artifact that will add a tag to a check-in. ** The git-fast-export file format might specify the same tag multiple ** times but only the last tag should be used. And we do not know which ** occurrence of the tag is the last until the import finishes. */ db_multi_exec( "CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT, tuuid TEXT);" "CREATE TEMP TABLE xbranch(tname TEXT UNIQUE, brnm TEXT);" "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);" ); git_fast_import(pIn); db_prepare(&q, "SELECT tcontent FROM xtag"); while( db_step(&q)==SQLITE_ROW ){ Blob record; db_ephemeral_blob(&q, 0, &record); fast_insert_content(&record, 0, 0); import_reset(0); } db_finalize(&q); } verify_cancel(); db_end_transaction(0); db_begin_transaction(); fossil_print("Rebuilding repository meta-data...\n"); rebuild_db(0, 1, !incrFlag); verify_cancel(); db_end_transaction(0); fossil_print("Vacuuming..."); fflush(stdout); |
︙ | ︙ |
Changes to src/info.c.
︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", db_column_int(&q, 1) ); fossil_print("%-13s %s %s\n", zType, zUuid, zDate); free(zDate); } db_finalize(&q); } zTags = info_tags_of_checkin(rid, 0); if( zTags && zTags[0] ){ fossil_print("tags: %s\n", zTags); } free(zTags); if( zComment ){ fossil_print("comment: "); | > > > > > > > > > > > > > > > > | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", db_column_int(&q, 1) ); fossil_print("%-13s %s %s\n", zType, zUuid, zDate); free(zDate); } db_finalize(&q); } if( zUuid ){ fossil_print("%-13s ", "leaf:"); if(is_a_leaf(rid)){ if(db_int(0, "SELECT 1 FROM tagxref AS tx" " WHERE tx.rid=%d" " AND tx.tagid=%d" " AND tx.tagtype>0", rid, TAG_CLOSED)){ fossil_print("%s\n", "closed"); }else{ fossil_print("%s\n", "open"); } }else{ fossil_print("no\n"); } } zTags = info_tags_of_checkin(rid, 0); if( zTags && zTags[0] ){ fossil_print("tags: %s\n", zTags); } free(zTags); if( zComment ){ fossil_print("comment: "); comment_print(zComment, 0, 14, -1, g.comFmtFlags); free(zComment); } } /* ** Print information about the URLs used to access a repository and ** checkouts in a repository. |
︙ | ︙ | |||
179 180 181 182 183 184 185 186 187 | */ void info_cmd(void){ i64 fsize; int verboseFlag = find_option("verbose","v",0)!=0; if( !verboseFlag ){ verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ } if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){ db_open_config(0); | > < > > | | | | | | | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | */ void info_cmd(void){ i64 fsize; int verboseFlag = find_option("verbose","v",0)!=0; if( !verboseFlag ){ verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ } if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){ db_open_config(0); db_open_repository(g.argv[2]); db_record_repository_filename(g.argv[2]); fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); fossil_print("project-code: %s\n", db_get("project-code", "<none>")); extraRepoInfo(); return; } db_find_and_open_repository(0,0); verify_all_options(); if( g.argc==2 ){ int vid; /* 012345678901234 */ db_record_repository_filename(0); fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); if( g.localOpen ){ fossil_print("repository: %s\n", db_repository_filename()); fossil_print("local-root: %s\n", g.zLocalRoot); } if( verboseFlag ) extraRepoInfo(); if( g.zConfigDbName ){ fossil_print("config-db: %s\n", g.zConfigDbName); } fossil_print("project-code: %s\n", db_get("project-code", "")); vid = g.localOpen ? db_lget_int("checkout", 0) : 0; if( vid ){ show_common_info(vid, "checkout:", 1, 1); } fossil_print("check-ins: %d\n", db_int(-1, "SELECT count(*) FROM event WHERE type='ci' /*scan*/")); }else{ int rid; rid = name_to_rid(g.argv[2]); if( rid==0 ){ fossil_fatal("no such object: %s\n", g.argv[2]); } show_common_info(rid, "uuid:", 1, 1); } } /* ** Show information about all tags on a given check-in. */ static void showTags(int rid){ Stmt q; int cnt = 0; db_prepare(&q, "SELECT tag.tagid, tagname, " " (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d)," " value, datetime(tagxref.mtime%s), tagtype," " (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)" " FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid" " WHERE tagxref.rid=%d" " ORDER BY tagname /*sort*/", rid, timeline_utc(), rid, rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zTagname = db_column_text(&q, 1); const char *zSrcUuid = db_column_text(&q, 2); const char *zValue = db_column_text(&q, 3); const char *zDate = db_column_text(&q, 4); int tagtype = db_column_int(&q, 5); |
︙ | ︙ | |||
285 286 287 288 289 290 291 292 293 294 295 296 297 298 | @ </li> } db_finalize(&q); if( cnt ){ @ </ul> } } /* ** Append the difference between artifacts to the output */ static void append_diff( const char *zFrom, /* Diff from this artifact */ | > > > > > > > > > > > > > > > > > > > > > > > | 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | @ </li> } db_finalize(&q); if( cnt ){ @ </ul> } } /* ** Show the context graph (immediate parents and children) for ** check-in rid. */ static void showContext(int rid){ Blob sql; Stmt q; @ <div class="section">Context</div> blob_zero(&sql); blob_append(&sql, timeline_query_for_www(), -1); db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" "INSERT INTO ok VALUES(%d);" "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;" "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rid, rid, rid ); blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC"); db_prepare(&q, "%s", blob_sql_text(&sql)); www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, rid, 0); db_finalize(&q); } /* ** Append the difference between artifacts to the output */ static void append_diff( const char *zFrom, /* Diff from this artifact */ |
︙ | ︙ | |||
360 361 362 363 364 365 366 | if( diffFlags ){ append_diff(zOld, zNew, diffFlags, pRe); } }else{ if( zOld && zNew ){ if( fossil_strcmp(zOld, zNew)!=0 ){ @ <p>Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a> | | | | | | | | 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 | if( diffFlags ){ append_diff(zOld, zNew, diffFlags, pRe); } }else{ if( zOld && zNew ){ if( fossil_strcmp(zOld, zNew)!=0 ){ @ <p>Modified %z(href("%R/finfo?name=%T",zName))%h(zName)</a> @ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> @ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>. }else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){ @ <p>Name change @ from %z(href("%R/finfo?name=%T",zOldName))%h(zOldName)</a> @ to %z(href("%R/finfo?name=%T",zName))%h(zName)</a>. }else{ @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") for @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a> } }else if( zOld ){ @ <p>Deleted %z(href("%R/finfo?name=%T",zName))%h(zName)</a> @ version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a> }else{ @ <p>Added %z(href("%R/finfo?name=%T",zName))%h(zName)</a> @ version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a> } if( diffFlags ){ append_diff(zOld, zNew, diffFlags, pRe); }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ @ @ %z(href("%R/fdiff?v1=%!S&v2=%!S&sbs=1",zOld,zNew))[diff]</a> } } } /* ** Generate javascript to enhance HTML diffs. */ |
︙ | ︙ | |||
425 426 427 428 429 430 431 | } /* ** Construct an appropriate diffFlag for text_diff() based on query ** parameters and the to boolean arguments. */ u64 construct_diff_flags(int verboseFlag, int sideBySide){ | | | < < | < < > > > > < | 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | } /* ** Construct an appropriate diffFlag for text_diff() based on query ** parameters and the to boolean arguments. */ u64 construct_diff_flags(int verboseFlag, int sideBySide){ u64 diffFlags = 0; /* Zero means do not show any diff */ if( verboseFlag!=0 ){ int x; if( sideBySide ){ diffFlags = DIFF_SIDEBYSIDE; /* "dw" query parameter determines width of each column */ x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1); if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK; diffFlags += x; } if( P("w") ){ diffFlags |= DIFF_IGNORE_ALLWS; } /* "dc" query parameter determines lines of context */ x = atoi(PD("dc","7")); if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK; diffFlags += x; /* The "noopt" parameter disables diff optimization */ if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT; diffFlags |= DIFF_STRIP_EOLCR; } return diffFlags; } /* ** WEBPAGE: vinfo ** WEBPAGE: ci ** URL: /ci?name=RID|ARTIFACTID ** ** Display information about a particular check-in. |
︙ | ︙ | |||
475 476 477 478 479 480 481 | void ci_page(void){ Stmt q1, q2, q3; int rid; int isLeaf; int verboseFlag; /* True to show diffs */ int sideBySide; /* True for side-by-side diffs */ u64 diffFlags; /* Flag parameter for text_diff() */ | | | > > > | | | | < | < | > | 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 | void ci_page(void){ Stmt q1, q2, q3; int rid; int isLeaf; int verboseFlag; /* True to show diffs */ int sideBySide; /* True for side-by-side diffs */ u64 diffFlags; /* Flag parameter for text_diff() */ const char *zName; /* Name of the check-in to be displayed */ const char *zUuid; /* UUID of zName */ const char *zParent; /* UUID of the parent check-in (if any) */ const char *zRe; /* regex parameter */ ReCompiled *pRe = 0; /* regex */ const char *zW; /* URL param for ignoring whitespace */ const char *zPage = "vinfo"; /* Page that shows diffs */ const char *zPageHide = "ci"; /* Page that hides diffs */ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } zName = P("name"); rid = name_to_rid_www("name"); if( rid==0 ){ style_header("Check-in Information Error"); @ No such object: %h(g.argv[2]) style_footer(); return; } zRe = P("regex"); if( zRe ) re_compile(&pRe, zRe, 0); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); zParent = db_text(0, "SELECT uuid FROM plink, blob" " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", rid ); isLeaf = is_a_leaf(rid); db_prepare(&q1, "SELECT uuid, datetime(mtime%s), user, comment," " datetime(omtime%s), mtime" " FROM blob, event" " WHERE blob.rid=%d" " AND event.objid=%d", timeline_utc(), timeline_utc(), rid, rid ); sideBySide = !is_false(PD("sbs","1")); if( db_step(&q1)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q1, 0); char *zEUser, *zEComment; const char *zUser; const char *zComment; const char *zDate; const char *zOrigDate; style_header("Check-in [%S]", zUuid); login_anonymous_available(); zEUser = db_text(0, "SELECT value FROM tagxref" " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_USER, rid); zEComment = db_text(0, "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", TAG_COMMENT, rid); zUser = db_column_text(&q1, 2); zComment = db_column_text(&q1, 3); zDate = db_column_text(&q1,1); |
︙ | ︙ | |||
554 555 556 557 558 559 560 | @ <tr><th>Original User:</th><td> hyperlink_to_user(zUser,zDate,"</td></tr>"); }else{ @ <tr><th>User:</th><td> hyperlink_to_user(zUser,zDate,"</td></tr>"); } if( zEComment ){ | | | | > > > | > > > > > > > > > | | | | | | | | | | > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < | | < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | | | < | | | | | | | | | < | > > > > > > | | > > > > | | > > > | | 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 | @ <tr><th>Original User:</th><td> hyperlink_to_user(zUser,zDate,"</td></tr>"); }else{ @ <tr><th>User:</th><td> hyperlink_to_user(zUser,zDate,"</td></tr>"); } if( zEComment ){ @ <tr><th>Edited Comment:</th><td class="infoComment">%!W(zEComment)</td></tr> @ <tr><th>Original Comment:</th><td class="infoComment">%!W(zComment)</td></tr> }else{ @ <tr><th>Comment:</th><td class="infoComment">%!W(zComment)</td></tr> } if( g.perm.Admin ){ db_prepare(&q2, "SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)" " FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)" " WHERE blob.rid=%d", rid ); if( db_step(&q2)==SQLITE_ROW ){ const char *zIpAddr = db_column_text(&q2, 0); const char *zUser = db_column_text(&q2, 1); const char *zDate = db_column_text(&q2, 2); if( zUser==0 || zUser[0]==0 ) zUser = "unknown"; @ <tr><th>Received From:</th> @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr> } db_finalize(&q2); } if( g.perm.Hyperlink ){ char *zPJ = db_get("short-project-name", 0); Blob projName; int jj; if( zPJ==0 ) zPJ = db_get("project-name", "unnamed"); blob_zero(&projName); blob_append(&projName, zPJ, -1); blob_trim(&projName); zPJ = blob_str(&projName); for(jj=0; zPJ[jj]; jj++){ if( (zPJ[jj]>0 && zPJ[jj]<' ') || strchr("\"*/:<>?\\|", zPJ[jj]) ){ zPJ[jj] = '_'; } } @ <tr><th>Timelines:</th><td> @ %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a> if( zParent ){ @ | %z(href("%R/timeline?p=%!S&unhide",zUuid))ancestors</a> } if( !isLeaf ){ @ | %z(href("%R/timeline?d=%!S&unhide",zUuid))descendants</a> } if( zParent && !isLeaf ){ @ | %z(href("%R/timeline?dp=%!S&unhide",zUuid))both</a> } db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag " " WHERE rid=%d AND tagtype>0 " " AND tag.tagid=tagxref.tagid " " AND +tag.tagname GLOB 'sym-*'", rid); while( db_step(&q2)==SQLITE_ROW ){ const char *zTagName = db_column_text(&q2, 0); @ | %z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</a> } db_finalize(&q2); /* The Download: line */ if( g.anon.Zip ){ char *zUrl = mprintf("%R/tarball/%t-%S.tar.gz?uuid=%s", zPJ, zUuid, zUuid); @ </td></tr> @ <tr><th>Downloads:</th><td> @ %z(href("%s",zUrl))Tarball</a> @ | %z(href("%R/zip/%t-%S.zip?uuid=%!S",zPJ,zUuid,zUuid)) @ ZIP archive</a> fossil_free(zUrl); } @ </td></tr> @ <tr><th>Other Links:</th> @ <td> @ %z(href("%R/tree?ci=%!S",zUuid))files</a> @ | %z(href("%R/fileage?name=%!S",zUuid))file ages</a> @ | %z(href("%R/tree?nofiles&type=tree&ci=%!S",zUuid))folders</a> @ | %z(href("%R/artifact/%!S",zUuid))manifest</a> if( g.anon.Write ){ @ | %z(href("%R/ci_edit?r=%!S",zUuid))edit</a> } @ </td> @ </tr> blob_reset(&projName); } @ </table> }else{ style_header("Check-in Information"); login_anonymous_available(); } db_finalize(&q1); showTags(rid); showContext(rid); @ <div class="section">Changes</div> @ <div class="sectionmenu"> verboseFlag = g.zPath[0]!='c'; if( db_get_boolean("show-version-diffs", 0)==0 ){ verboseFlag = !verboseFlag; zPage = "ci"; zPageHide = "vinfo"; } diffFlags = construct_diff_flags(verboseFlag, sideBySide); zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; if( verboseFlag ){ @ %z(xhref("class='button'","%R/%s/%T",zPageHide,zName)) @ Hide Diffs</a> if( sideBySide ){ @ %z(xhref("class='button'","%R/%s/%T?sbs=0%s",zPage,zName,zW)) @ Unified Diffs</a> }else{ @ %z(xhref("class='button'","%R/%s/%T?sbs=1%s",zPage,zName,zW)) @ Side-by-Side Diffs</a> } if( *zW ){ @ %z(xhref("class='button'","%R/%s/%T?sbs=%d",zPage,zName,sideBySide)) @ Show Whitespace Changes</a> }else{ @ %z(xhref("class='button'","%R/%s/%T?sbs=%d&w",zPage,zName,sideBySide)) @ Ignore Whitespace</a> } }else{ @ %z(xhref("class='button'","%R/%s/%T?sbs=0",zPage,zName)) @ Show Unified Diffs</a> @ %z(xhref("class='button'","%R/%s/%T?sbs=1",zPage,zName)) @ Show Side-by-Side Diffs</a> } if( zParent ){ @ %z(xhref("class='button'","%R/vpatch?from=%!S&to=%!S",zParent,zUuid)) @ Patch</a> } @</div> if( pRe ){ @ <p><b>Only differences that match regular expression "%h(zRe)" @ are shown.</b></p> } db_prepare(&q3, "SELECT name," " mperm," " (SELECT uuid FROM blob WHERE rid=mlink.pid)," " (SELECT uuid FROM blob WHERE rid=mlink.fid)," " (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)" " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" " WHERE mlink.mid=%d AND NOT mlink.isaux" " AND (mlink.fid>0" " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))" " ORDER BY name /*sort*/", rid, rid ); while( db_step(&q3)==SQLITE_ROW ){ const char *zName = db_column_text(&q3,0); int mperm = db_column_int(&q3, 1); const char *zOld = db_column_text(&q3,2); const char *zNew = db_column_text(&q3,3); const char *zOldName = db_column_text(&q3, 4); append_file_change_line(zName, zOld, zNew, zOldName, diffFlags,pRe,mperm); } db_finalize(&q3); append_diff_javascript(sideBySide); style_footer(); } /* ** WEBPAGE: winfo ** URL: /winfo?name=UUID ** ** Return information about a wiki page. */ void winfo_page(void){ int rid; Manifest *pWiki; char *zUuid; char *zDate; Blob wiki; int modPending; const char *zModAction; login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } rid = name_to_rid_www("name"); if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))==0 ){ style_header("Wiki Page Information Error"); @ No such object: %h(P("name")) style_footer(); return; } if( g.perm.ModWiki && (zModAction = P("modaction"))!=0 ){ if( strcmp(zModAction,"delete")==0 ){ moderation_disapprove(rid); /* ** Next, check if the wiki page still exists; if not, we cannot ** redirect to it. */ if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){ cgi_redirectf("%R/wiki?name=%T", pWiki->zWikiTitle); /*NOTREACHED*/ }else{ cgi_redirectf("%R/modreq"); /*NOTREACHED*/ } } if( strcmp(zModAction,"approve")==0 ){ moderation_approve(rid); } } style_header("Update of \"%h\"", pWiki->zWikiTitle); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); zDate = db_text(0, "SELECT datetime(%.17g)", pWiki->rDate); style_submenu_element("Raw", "Raw", "artifact/%s", zUuid); style_submenu_element("History", "History", "whistory?name=%t", pWiki->zWikiTitle); style_submenu_element("Page", "Page", "wiki?name=%t", pWiki->zWikiTitle); login_anonymous_available(); @ <div class="section">Overview</div> @ <p><table class="label-value"> @ <tr><th>Artifact ID:</th> @ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a> if( g.perm.Setup ){ @ (%d(rid)) } modPending = moderation_pending(rid); if( modPending ){ @ <span class="modpending">*** Awaiting Moderator Approval ***</span> } @ </td></tr> @ <tr><th>Page Name:</th><td>%h(pWiki->zWikiTitle)</td></tr> @ <tr><th>Date:</th><td> hyperlink_to_date(zDate, "</td></tr>"); @ <tr><th>Original User:</th><td> hyperlink_to_user(pWiki->zUser, zDate, "</td></tr>"); if( pWiki->zMimetype ){ @ <tr><th>Mimetype:</th><td>%h(pWiki->zMimetype)</td></tr> } if( pWiki->nParent>0 ){ int i; @ <tr><th>Parent%s(pWiki->nParent==1?"":"s"):</th><td> for(i=0; i<pWiki->nParent; i++){ char *zParent = pWiki->azParent[i]; @ %z(href("info/%!S",zParent))%s(zParent)</a> } @ </td></tr> } @ </table> if( g.perm.ModWiki && modPending ){ @ <div class="section">Moderation</div> |
︙ | ︙ | |||
790 791 792 793 794 795 796 | @ </form> @ </blockquote> } @ <div class="section">Content</div> blob_init(&wiki, pWiki->zWiki, -1); | | | 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 | @ </form> @ </blockquote> } @ <div class="section">Content</div> blob_init(&wiki, pWiki->zWiki, -1); wiki_render_by_mimetype(&wiki, pWiki->zMimetype); blob_reset(&wiki); manifest_destroy(pWiki); style_footer(); } /* ** Show a webpage error message |
︙ | ︙ | |||
812 813 814 815 816 817 818 | style_header("URL Error"); @ <h1>Error</h1> @ <p>%h(z)</p> style_footer(); } /* | | | | 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 | style_header("URL Error"); @ <h1>Error</h1> @ <p>%h(z)</p> style_footer(); } /* ** Find an check-in based on query parameter zParam and parse its ** manifest. Return the number of errors. */ static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){ int rid; *pRid = rid = name_to_rid_www(zParam); if( rid==0 ){ const char *z = P(zParam); if( z==0 || z[0]==0 ){ webpage_error("Missing \"%s\" query parameter.", zParam); }else{ webpage_error("No such artifact: \"%s\"", z); } return 0; } if( !is_a_version(rid) ){ webpage_error("Artifact %s is not a check-in.", P(zParam)); return 0; } return manifest_get(rid, CFTYPE_MANIFEST, 0); } /* ** Output a description of a check-in |
︙ | ︙ | |||
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 | @ tags: %s(blob_str(&links)), blob_reset(&links); }else{ @ tags: %h(zTagList), } @ date: hyperlink_to_date(zDate, ")"); } db_finalize(&q); } /* ** WEBPAGE: vdiff ** URL: /vdiff ** ** Query parameters: ** | > | | | | | > | > > < | < | 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 | @ tags: %s(blob_str(&links)), blob_reset(&links); }else{ @ tags: %h(zTagList), } @ date: hyperlink_to_date(zDate, ")"); tag_private_status(rid); } db_finalize(&q); } /* ** WEBPAGE: vdiff ** URL: /vdiff ** ** Query parameters: ** ** from=TAG Left side of the comparison ** to=TAG Right side of the comparison ** branch=TAG Show all changes on a particular branch ** v=BOOLEAN Default true. If false, only list files that have changed ** sbs=BOOLEAN Side-by-side diff if true. Unified diff if false ** glob=STRING only diff files matching this glob ** ** ** Show all differences between two check-ins. */ void vdiff_page(void){ int ridFrom, ridTo; int verboseFlag = 0; int sideBySide = 0; u64 diffFlags = 0; Manifest *pFrom, *pTo; ManifestFile *pFileFrom, *pFileTo; const char *zBranch; const char *zFrom; const char *zTo; const char *zRe; const char *zW; const char *zVerbose; const char *zGlob; ReCompiled *pRe = 0; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } login_anonymous_available(); zRe = P("regex"); if( zRe ) re_compile(&pRe, zRe, 0); zBranch = P("branch"); if( zBranch && zBranch[0] ){ cgi_replace_parameter("from", mprintf("root:%s", zBranch)); cgi_replace_parameter("to", zBranch); } |
︙ | ︙ | |||
948 949 950 951 952 953 954 955 956 957 | zVerbose = P("verbose"); } if( !zVerbose ){ zVerbose = P("detail"); /* deprecated */ } verboseFlag = (zVerbose!=0) && !is_false(zVerbose); if( !verboseFlag && sideBySide ) verboseFlag = 1; zFrom = P("from"); zTo = P("to"); if( !sideBySide ){ | > > > > > > > > > > > > > > | | | > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > < > | | > > | | | > < > > | | | | > | 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 | zVerbose = P("verbose"); } if( !zVerbose ){ zVerbose = P("detail"); /* deprecated */ } verboseFlag = (zVerbose!=0) && !is_false(zVerbose); if( !verboseFlag && sideBySide ) verboseFlag = 1; zGlob = P("glob"); zFrom = P("from"); zTo = P("to"); if(zGlob && !*zGlob){ zGlob = NULL; } diffFlags = construct_diff_flags(verboseFlag, sideBySide); zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; style_submenu_element("Path","path", "%R/timeline?me=%T&you=%T", zFrom, zTo); if( sideBySide || verboseFlag ){ style_submenu_element("Hide Diff", "hidediff", "%R/vdiff?from=%T&to=%T&sbs=0%s%T%s", zFrom, zTo, zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); } if( !sideBySide ){ style_submenu_element("Side-by-Side Diff", "sbsdiff", "%R/vdiff?from=%T&to=%T&sbs=1%s%T%s", zFrom, zTo, zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); } if( sideBySide || !verboseFlag ) { style_submenu_element("Unified Diff", "udiff", "%R/vdiff?from=%T&to=%T&sbs=0&v%s%T%s", zFrom, zTo, zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); } style_submenu_element("Invert", "invert", "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T%s", zTo, zFrom, sideBySide, (verboseFlag && !sideBySide)?"&v":"", zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW); if( zGlob ){ style_submenu_element("Clear glob", "clearglob", "%R/vdiff?from=%T&to=%T&sbs=%d%s%s", zFrom, zTo, sideBySide, (verboseFlag && !sideBySide)?"&v":"", zW); }else{ style_submenu_element("Patch", "patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW); } if( sideBySide || verboseFlag ){ if( *zW ){ style_submenu_element("Show Whitespace Differences", "whitespace", "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T", zFrom, zTo, sideBySide, (verboseFlag && !sideBySide)?"&v":"", zGlob ? "&glob=" : "", zGlob ? zGlob : ""); }else{ style_submenu_element("Ignore Whitespace", "ignorews", "%R/vdiff?from=%T&to=%T&sbs=%d%s%s%T&w", zFrom, zTo, sideBySide, (verboseFlag && !sideBySide)?"&v":"", zGlob ? "&glob=" : "", zGlob ? zGlob : ""); } } style_header("Check-in Differences"); @ <h2>Difference From:</h2><blockquote> checkin_description(ridFrom); @ </blockquote><h2>To:</h2><blockquote> checkin_description(ridTo); @ </blockquote> if( pRe ){ @ <p><b>Only differences that match regular expression "%h(zRe)" @ are shown.</b></p> } if( zGlob ){ @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p> } @<hr /><p> manifest_file_rewind(pFrom); pFileFrom = manifest_file_next(pFrom, 0); manifest_file_rewind(pTo); pFileTo = manifest_file_next(pTo, 0); while( pFileFrom || pFileTo ){ int cmp; if( pFileFrom==0 ){ cmp = +1; }else if( pFileTo==0 ){ cmp = -1; }else{ cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); } if( cmp<0 ){ if( !zGlob || sqlite3_strglob(zGlob, pFileFrom->zName)==0 ){ append_file_change_line(pFileFrom->zName, pFileFrom->zUuid, 0, 0, diffFlags, pRe, 0); } pFileFrom = manifest_file_next(pFrom, 0); }else if( cmp>0 ){ if( !zGlob || sqlite3_strglob(zGlob, pFileTo->zName)==0 ){ append_file_change_line(pFileTo->zName, 0, pFileTo->zUuid, 0, diffFlags, pRe, manifest_file_mperm(pFileTo)); } pFileTo = manifest_file_next(pTo, 0); }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ pFileFrom = manifest_file_next(pFrom, 0); pFileTo = manifest_file_next(pTo, 0); }else{ if(!zGlob || (sqlite3_strglob(zGlob, pFileFrom->zName)==0 || sqlite3_strglob(zGlob, pFileTo->zName)==0) ){ append_file_change_line(pFileFrom->zName, pFileFrom->zUuid, pFileTo->zUuid, 0, diffFlags, pRe, manifest_file_mperm(pFileTo)); } pFileFrom = manifest_file_next(pFrom, 0); pFileTo = manifest_file_next(pTo, 0); } } manifest_destroy(pFrom); manifest_destroy(pTo); append_diff_javascript(sideBySide); |
︙ | ︙ | |||
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | #define OBJTYPE_WIKI 0x0004 #define OBJTYPE_TICKET 0x0008 #define OBJTYPE_ATTACHMENT 0x0010 #define OBJTYPE_EVENT 0x0020 #define OBJTYPE_TAG 0x0040 #define OBJTYPE_SYMLINK 0x0080 #define OBJTYPE_EXE 0x0100 #endif /* ** Write a description of an object to the www reply. ** ** If the object is a file then mention: ** ** * It's artifact ID ** * All its filenames ** * The check-in it was part of, with times and users ** ** If the object is a manifest, then mention: ** ** * It's artifact ID ** * date of check-in ** * Comment & user */ int object_description( int rid, /* The artifact ID */ | > > > > > > | | | 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 | #define OBJTYPE_WIKI 0x0004 #define OBJTYPE_TICKET 0x0008 #define OBJTYPE_ATTACHMENT 0x0010 #define OBJTYPE_EVENT 0x0020 #define OBJTYPE_TAG 0x0040 #define OBJTYPE_SYMLINK 0x0080 #define OBJTYPE_EXE 0x0100 /* ** Possible flags for the second parameter to ** object_description() */ #define OBJDESC_DETAIL 0x0001 /* more detail */ #endif /* ** Write a description of an object to the www reply. ** ** If the object is a file then mention: ** ** * It's artifact ID ** * All its filenames ** * The check-in it was part of, with times and users ** ** If the object is a manifest, then mention: ** ** * It's artifact ID ** * date of check-in ** * Comment & user */ int object_description( int rid, /* The artifact ID */ u32 objdescFlags, /* Flags to control display */ Blob *pDownloadName /* Fill with an appropriate download name */ ){ Stmt q; int cnt = 0; int nWiki = 0; int objType = 0; char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); int showDetail = (objdescFlags & OBJDESC_DETAIL)!=0; char *prevName = 0; db_prepare(&q, "SELECT filename.name, datetime(event.mtime)," " coalesce(event.ecomment,event.comment)," " coalesce(event.euser,event.user)," " b.uuid, mlink.mperm," |
︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 | const char *zName = db_column_text(&q, 0); const char *zDate = db_column_text(&q, 1); const char *zCom = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); const char *zVers = db_column_text(&q, 4); int mPerm = db_column_int(&q, 5); const char *zBr = db_column_text(&q, 6); | | > > > > > > > > | > > | > > | | | | > > > > > > | > | | > | | 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 | const char *zName = db_column_text(&q, 0); const char *zDate = db_column_text(&q, 1); const char *zCom = db_column_text(&q, 2); const char *zUser = db_column_text(&q, 3); const char *zVers = db_column_text(&q, 4); int mPerm = db_column_int(&q, 5); const char *zBr = db_column_text(&q, 6); int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0; if( sameFilename && !showDetail ){ if( cnt==1 ){ @ %z(href("%R/whatis/%!S",zUuid))[more...]</a> } cnt++; continue; } if( !sameFilename ){ if( prevName && showDetail ) { @ </ul> } if( mPerm==PERM_LNK ){ @ <li>Symbolic link objType |= OBJTYPE_SYMLINK; }else if( mPerm==PERM_EXE ){ @ <li>Executable file objType |= OBJTYPE_EXE; }else{ @ <li>File } objType |= OBJTYPE_CONTENT; @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a> tag_private_status(rid); if( showDetail ){ @ <ul> } prevName = fossil_strdup(zName); } if( showDetail ){ @ <li> hyperlink_to_date(zDate,""); @ — part of check-in hyperlink_to_uuid(zVers); }else{ @ — part of check-in hyperlink_to_uuid(zVers); @ at hyperlink_to_date(zDate,""); } if( zBr && zBr[0] ){ @ on branch %z(href("%R/timeline?r=%T",zBr))%h(zBr)</a> } @ — %!W(zCom) (user: hyperlink_to_user(zUser,zDate,")"); if( g.perm.Hyperlink ){ @ %z(href("%R/finfo?name=%T&ci=%!S",zName,zVers))[ancestry]</a> @ %z(href("%R/annotate?filename=%T&checkin=%!S",zName,zVers)) @ [annotate]</a> @ %z(href("%R/blame?filename=%T&checkin=%!S",zName,zVers)) @ [blame]</a> } cnt++; if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_append(pDownloadName, zName, -1); } } if( prevName && showDetail ){ @ </ul> } @ </ul> free(prevName); db_finalize(&q); db_prepare(&q, "SELECT substr(tagname, 6, 10000), datetime(event.mtime)," |
︙ | ︙ | |||
1194 1195 1196 1197 1198 1199 1200 | hyperlink_to_event_tagid(db_column_int(&q, 5)); }else{ @ Control file referencing } if( zType[0]!='e' ){ hyperlink_to_uuid(zUuid); } | | | > | 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 | hyperlink_to_event_tagid(db_column_int(&q, 5)); }else{ @ Control file referencing } if( zType[0]!='e' ){ hyperlink_to_uuid(zUuid); } @ - %!W(zCom) by hyperlink_to_user(zUser,zDate," on"); hyperlink_to_date(zDate, "."); if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_appendf(pDownloadName, "%S.txt", zUuid); } tag_private_status(rid); cnt++; } db_finalize(&q); } db_prepare(&q, "SELECT target, filename, datetime(mtime), user, src" " FROM attachment" |
︙ | ︙ | |||
1224 1225 1226 1227 1228 1229 1230 | if( cnt>0 ){ @ Also attachment "%h(zFilename)" to }else{ @ Attachment "%h(zFilename)" to } objType |= OBJTYPE_ATTACHMENT; if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){ | | | | > | | < > > > > > > | > | > > > > > > > > > > | | | | | | | | | | | > | > > > > > | 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 | if( cnt>0 ){ @ Also attachment "%h(zFilename)" to }else{ @ Attachment "%h(zFilename)" to } objType |= OBJTYPE_ATTACHMENT; if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){ if( g.perm.Hyperlink && g.anon.RdTkt ){ @ ticket [%z(href("%R/tktview?name=%!S",zTarget))%S(zTarget)</a>] }else{ @ ticket [%S(zTarget)] } }else{ if( g.perm.Hyperlink && g.anon.RdWiki ){ @ wiki page [%z(href("%R/wiki?name=%t",zTarget))%h(zTarget)</a>] }else{ @ wiki page [%h(zTarget)] } } @ added by hyperlink_to_user(zUser,zDate," on"); hyperlink_to_date(zDate,"."); cnt++; if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_append(pDownloadName, zFilename, -1); } tag_private_status(rid); } db_finalize(&q); if( cnt==0 ){ @ Control artifact. if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_appendf(pDownloadName, "%S.txt", zUuid); } tag_private_status(rid); } return objType; } /* ** WEBPAGE: fdiff ** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN®ex=REGEX ** ** Two arguments, v1 and v2, identify the files to be diffed. Show the ** difference between the two artifacts. Show diff side by side unless sbs ** is 0. Generate plaintext if "patch" is present. ** ** Additional parameters: ** ** verbose Show more detail when describing artifacts */ void diff_page(void){ int v1, v2; int isPatch; int sideBySide; char *zV1; char *zV2; const char *zRe; const char *zW; /* URL param for ignoring whitespace */ ReCompiled *pRe = 0; u64 diffFlags; u32 objdescFlags = 0; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } v1 = name_to_rid_www("v1"); v2 = name_to_rid_www("v2"); if( v1==0 || v2==0 ) fossil_redirect_home(); zRe = P("regex"); if( zRe ) re_compile(&pRe, zRe, 0); if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; isPatch = P("patch")!=0; if( isPatch ){ Blob c1, c2, *pOut; pOut = cgi_output_blob(); cgi_set_content_type("text/plain"); diffFlags = 4; content_get(v1, &c1); content_get(v2, &c2); text_diff(&c1, &c2, pOut, pRe, diffFlags); blob_reset(&c1); blob_reset(&c2); return; } sideBySide = !is_false(PD("sbs","1")); zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1); zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2); diffFlags = construct_diff_flags(1, sideBySide) | DIFF_HTML; style_header("Diff"); zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; if( *zW ){ style_submenu_element("Show Whitespace Changes", "Show Whitespace Changes", "%s/fdiff?v1=%T&v2=%T&sbs=%d", g.zTop, P("v1"), P("v2"), sideBySide); }else{ style_submenu_element("Ignore Whitespace", "Ignore Whitespace", "%s/fdiff?v1=%T&v2=%T&sbs=%d&w", g.zTop, P("v1"), P("v2"), sideBySide); } style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch", g.zTop, P("v1"), P("v2")); if( !sideBySide ){ style_submenu_element("Side-by-Side Diff", "sbsdiff", "%s/fdiff?v1=%T&v2=%T&sbs=1%s", g.zTop, P("v1"), P("v2"), zW); }else{ style_submenu_element("Unified Diff", "udiff", "%s/fdiff?v1=%T&v2=%T&sbs=0%s", g.zTop, P("v1"), P("v2"), zW); } if( P("smhdr")!=0 ){ @ <h2>Differences From Artifact @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2> }else{ @ <h2>Differences From @ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2> object_description(v1, objdescFlags, 0); @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2> object_description(v2, objdescFlags, 0); } if( pRe ){ @ <b>Only differences that match regular expression "%h(zRe)" @ are shown.</b> } @ <hr /> append_diff(zV1, zV2, diffFlags, pRe); append_diff_javascript(sideBySide); style_footer(); } /* ** WEBPAGE: raw ** URL: /raw?name=ARTIFACTID&m=TYPE ** ** Return the uninterpreted content of an artifact. Used primarily ** to view artifacts that are images. */ void rawartifact_page(void){ int rid; char *zUuid; const char *zMime; Blob content; rid = name_to_rid_www("name"); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( rid==0 ) fossil_redirect_home(); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); if( fossil_strcmp(P("name"), zUuid)==0 && login_is_nobody() ){ g.isConst = 1; } free(zUuid); zMime = P("m"); if( zMime==0 ){ char *zFName = db_text(0, "SELECT filename.name FROM mlink, filename" " WHERE mlink.fid=%d" " AND filename.fnid=mlink.fnid", rid); if( !zFName ){ /* Look also at the attachment table */ |
︙ | ︙ | |||
1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 | /* ** WEBPAGE: hexdump ** URL: /hexdump?name=ARTIFACTID ** ** Show the complete content of a file identified by ARTIFACTID ** as preformatted text. */ void hexdump_page(void){ int rid; Blob content; Blob downloadName; char *zUuid; rid = name_to_rid_www("name"); login_check_credentials(); | > > > > > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 | /* ** WEBPAGE: hexdump ** URL: /hexdump?name=ARTIFACTID ** ** Show the complete content of a file identified by ARTIFACTID ** as preformatted text. ** ** Other parameters: ** ** verbose Show more detail when describing the object */ void hexdump_page(void){ int rid; Blob content; Blob downloadName; char *zUuid; u32 objdescFlags = 0; rid = name_to_rid_www("name"); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( rid==0 ) fossil_redirect_home(); if( g.perm.Admin ){ const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ style_submenu_element("Unshun","Unshun", "%s/shun?accept=%s&sub=1#delshun", g.zTop, zUuid); }else{ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); } } style_header("Hex Artifact Content"); zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid); if( g.perm.Setup ){ @ <h2>Artifact %s(zUuid) (%d(rid)):</h2> }else{ @ <h2>Artifact %s(zUuid):</h2> } blob_zero(&downloadName); if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; object_description(rid, objdescFlags, &downloadName); style_submenu_element("Download", "Download", "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid); @ <hr /> content_get(rid, &content); @ <blockquote><pre> hexdump(&content); @ </pre></blockquote> style_footer(); } /* ** Attempt to lookup the specified check-in and file name into an rid. */ int artifact_from_ci_and_filename( const char *zCI, const char *zFilename ){ int cirid; Manifest *pManifest; ManifestFile *pFile; if( zCI==0 ) return 0; if( zFilename==0 ) return 0; cirid = name_to_rid(zCI); pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0); if( pManifest==0 ) return 0; manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ if( fossil_strcmp(zFilename, pFile->zName)==0 ){ int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); manifest_destroy(pManifest); return rid; } } return 0; } /* ** Look for "ci" and "filename" query parameters. If found, try to ** use them to extract the record ID of an artifact for the file. */ int artifact_from_ci_and_filename_www(void){ const char *zFilename; const char *zCI; int cirid; Manifest *pManifest; ManifestFile *pFile; zCI = P("ci"); |
︙ | ︙ | |||
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 | /* ** The "z" argument is a string that contains the text of a source code ** file. This routine appends that text to the HTTP reply with line numbering. ** ** zLn is the ?ln= parameter for the HTTP query. If there is an argument, ** then highlight that line number and scroll to it once the page loads. ** If there are two line numbers, highlight the range of lines. */ void output_text_with_line_numbers( const char *z, const char *zLn ){ int iStart, iEnd; /* Start and end of region to highlight */ int n = 0; /* Current line number */ | > > | > > > > | | | | | > | > | > > > > > > > > > > > > > > > > > > > | | > > | > > > | | > > > > | | | > | | | | | > > > > > > > | | | | | | | | | | | | < < < < < > > > > | | | | | | | | | | | | | | | | > | | > > > > > > | | > > > > | | | | | | | 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 | /* ** The "z" argument is a string that contains the text of a source code ** file. This routine appends that text to the HTTP reply with line numbering. ** ** zLn is the ?ln= parameter for the HTTP query. If there is an argument, ** then highlight that line number and scroll to it once the page loads. ** If there are two line numbers, highlight the range of lines. ** Multiple ranges can be highlighed by adding additional line numbers ** separated by a non-digit character (also not one of [-,.]). */ void output_text_with_line_numbers( const char *z, const char *zLn ){ int iStart, iEnd; /* Start and end of region to highlight */ int n = 0; /* Current line number */ int i = 0; /* Loop index */ int iTop = 0; /* Scroll so that this line is on top of screen. */ Stmt q; iStart = iEnd = atoi(zLn); db_multi_exec( "CREATE TEMP TABLE lnos(iStart INTEGER PRIMARY KEY, iEnd INTEGER)"); if( iStart>0 ){ do{ while( fossil_isdigit(zLn[i]) ) i++; if( zLn[i]==',' || zLn[i]=='-' || zLn[i]=='.' ){ i++; while( zLn[i]=='.' ){ i++; } iEnd = atoi(&zLn[i]); while( fossil_isdigit(zLn[i]) ) i++; } while( fossil_isdigit(zLn[i]) ) i++; if( iEnd<iStart ) iEnd = iStart; db_multi_exec( "INSERT OR REPLACE INTO lnos VALUES(%d,%d)", iStart, iEnd ); iStart = iEnd = atoi(&zLn[i++]); }while( zLn[i] && iStart && iEnd ); } db_prepare(&q, "SELECT min(iStart), iEnd FROM lnos"); if( db_step(&q)==SQLITE_ROW ){ iStart = db_column_int(&q, 0); iEnd = db_column_int(&q, 1); iTop = iStart - 15 + (iEnd-iStart)/4; if( iTop>iStart - 2 ) iTop = iStart-2; } db_finalize(&q); @ <pre> while( z[0] ){ n++; db_prepare(&q, "SELECT min(iStart), max(iEnd) FROM lnos" " WHERE iStart <= %d AND iEnd >= %d", n, n); if( db_step(&q)==SQLITE_ROW ){ iStart = db_column_int(&q, 0); iEnd = db_column_int(&q, 1); } db_finalize(&q); for(i=0; z[i] && z[i]!='\n'; i++){} if( n==iTop ) cgi_append_content("<span id=\"topln\">", -1); if( n==iStart ){ cgi_append_content("<div class=\"selectedText\">",-1); } cgi_printf("%6d ", n); if( i>0 ){ char *zHtml = htmlize(z, i); cgi_append_content(zHtml, -1); fossil_free(zHtml); } if( n==iTop ) cgi_append_content("</span>", -1); if( n==iEnd ) cgi_append_content("</div>", -1); else cgi_append_content("\n", 1); z += i; if( z[0]=='\n' ) z++; } if( n<iEnd ) cgi_printf("</div>"); @ </pre> if( db_int(0, "SELECT EXISTS(SELECT 1 FROM lnos)") ){ @ <script>gebi('topln').scrollIntoView(true);</script> } } /* ** WEBPAGE: whatis ** WEBPAGE: artifact ** ** URL: /artifact/SHA1HASH ** URL: /artifact?ci=CHECKIN&filename=PATH ** URL: /whatis/SHA1HASH ** ** Additional query parameters: ** ** ln - show line numbers ** ln=N - highlight line number N ** ln=M-N - highlight lines M through N inclusive ** ln=M-N+Y-Z - higllight lines M through N and Y through Z (inclusive) ** verbose - show more detail in the description ** ** The /artifact page show the complete content of a file ** identified by SHA1HASH as preformatted text. The ** /whatis page shows only a description of the file. */ void artifact_page(void){ int rid = 0; Blob content; const char *zMime; Blob downloadName; int renderAsWiki = 0; int renderAsHtml = 0; int objType; int asText; const char *zUuid; u32 objdescFlags = 0; int descOnly = fossil_strcmp(g.zPath,"whatis")==0; const char *zLn = P("ln"); if( P("ci") && P("filename") ){ rid = artifact_from_ci_and_filename_www(); } if( rid==0 ){ rid = name_to_rid_www("name"); } login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if( rid==0 ) fossil_redirect_home(); if( g.perm.Admin ){ const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ style_submenu_element("Unshun","Unshun", "%s/shun?accept=%s&sub=1#accshun", g.zTop, zUuid); }else{ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); } } if( descOnly || P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL; style_header("%s", descOnly ? "Artifact Description" : "Artifact Content"); zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); if( g.perm.Setup ){ @ <h2>Artifact %s(zUuid) (%d(rid)):</h2> }else{ @ <h2>Artifact %s(zUuid):</h2> } blob_zero(&downloadName); objType = object_description(rid, objdescFlags, &downloadName); style_submenu_element("Download", "Download", "%R/raw/%T?name=%s", blob_str(&downloadName), zUuid); if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){ style_submenu_element("Check-ins Using", "Check-ins Using", "%R/timeline?n=200&uf=%s",zUuid); } asText = P("txt")!=0; zMime = mimetype_from_name(blob_str(&downloadName)); if( zMime ){ if( fossil_strcmp(zMime, "text/html")==0 ){ if( asText ){ style_submenu_element("Html", "Html", "%s/artifact/%s", g.zTop, zUuid); }else{ renderAsHtml = 1; style_submenu_element("Text", "Text", "%s/artifact/%s?txt=1", g.zTop, zUuid); } }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 || fossil_strcmp(zMime, "text/x-markdown")==0 ){ if( asText ){ style_submenu_element("Wiki", "Wiki", "%s/artifact/%s", g.zTop, zUuid); }else{ renderAsWiki = 1; style_submenu_element("Text", "Text", "%s/artifact/%s?txt=1", g.zTop, zUuid); } } } if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){ style_submenu_element("Parsed", "Parsed", "%R/info/%s", zUuid); } if( descOnly ){ style_submenu_element("Content", "Content", "%R/artifact/%s", zUuid); }else{ style_submenu_element("Line Numbers", "Line Numbers", "%R/info/%s%s",zUuid, ((zLn&&*zLn) ? "" : "?txt=1&ln=0")); @ <hr /> content_get(rid, &content); if( renderAsWiki ){ wiki_render_by_mimetype(&content, zMime); }else if( renderAsHtml ){ @ <iframe src="%R/raw/%T(blob_str(&downloadName))?name=%s(zUuid)" @ width="100%%" frameborder="0" marginwidth="0" marginheight="0" @ sandbox="allow-same-origin" @ onload="this.height = this.contentDocument.documentElement.scrollHeight;"> @ </iframe> }else{ style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid); blob_to_utf8_no_bom(&content, 0); zMime = mimetype_from_content(&content); @ <blockquote> if( zMime==0 ){ const char *z; z = blob_str(&content); if( zLn ){ output_text_with_line_numbers(z, zLn); }else{ @ <pre> @ %h(z) @ </pre> } }else if( strncmp(zMime, "image/", 6)==0 ){ @ <img src="%R/raw/%s(zUuid)?m=%s(zMime)" /> style_submenu_element("Image", "Image", "%R/raw/%s?m=%s", zUuid, zMime); }else{ @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i> } @ </blockquote> } } style_footer(); } /* ** WEBPAGE: tinfo ** URL: /tinfo?name=ARTIFACTID ** ** Show the details of a ticket change control artifact. */ void tinfo_page(void){ int rid; char *zDate; const char *zUuid; char zTktName[UUID_SIZE+1]; Manifest *pTktChng; int modPending; const char *zModAction; char *zTktTitle; login_check_credentials(); if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } rid = name_to_rid_www("name"); if( rid==0 ){ fossil_redirect_home(); } zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( g.perm.Admin ){ if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ style_submenu_element("Unshun","Unshun", "%s/shun?accept=%s&sub=1#accshun", g.zTop, zUuid); }else{ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); } } pTktChng = manifest_get(rid, CFTYPE_TICKET, 0); if( pTktChng==0 ) fossil_redirect_home(); zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate); memcpy(zTktName, pTktChng->zTicketUuid, UUID_SIZE+1); if( g.perm.ModTkt && (zModAction = P("modaction"))!=0 ){ if( strcmp(zModAction,"delete")==0 ){ moderation_disapprove(rid); /* ** Next, check if the ticket still exists; if not, we cannot ** redirect to it. */ if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zTktName) ){ cgi_redirectf("%R/tktview/%s", zTktName); /*NOTREACHED*/ }else{ cgi_redirectf("%R/modreq"); /*NOTREACHED*/ } } if( strcmp(zModAction,"approve")==0 ){ moderation_approve(rid); } } zTktTitle = db_table_has_column("repository", "ticket", "title" ) ? db_text("(No title)", "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName) : 0; style_header("Ticket Change Details"); style_submenu_element("Raw", "Raw", "%R/artifact/%s", zUuid); style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName); style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName); style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName); if( P("plaintext") ){ style_submenu_element("Formatted", "Formatted", "%R/info/%s", zUuid); }else{ style_submenu_element("Plaintext", "Plaintext", "%R/info/%s?plaintext", zUuid); } @ <div class="section">Overview</div> @ <p><table class="label-value"> @ <tr><th>Artifact ID:</th> @ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a> if( g.perm.Setup ){ @ (%d(rid)) } modPending = moderation_pending(rid); if( modPending ){ @ <span class="modpending">*** Awaiting Moderator Approval ***</span> } @ <tr><th>Ticket:</th> @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a> if( zTktTitle ){ @<br>%h(zTktTitle) } @</td></tr> @ <tr><th>Date:</th><td> hyperlink_to_date(zDate, "</td></tr>"); @ <tr><th>User:</th><td> hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>"); |
︙ | ︙ | |||
1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 | ** Figure out what the artifact ID is and jump to it. */ void info_page(void){ const char *zName; Blob uuid; int rid; int rc; zName = P("name"); if( zName==0 ) fossil_redirect_home(); | > > > > > > > > > > | | | | | | > | | | | < < < > > > > | | 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 | ** Figure out what the artifact ID is and jump to it. */ void info_page(void){ const char *zName; Blob uuid; int rid; int rc; int nLen; zName = P("name"); if( zName==0 ) fossil_redirect_home(); nLen = strlen(zName); blob_set(&uuid, zName); if( name_collisions(zName) ){ cgi_set_parameter("src","info"); ambiguous_page(); return; } rc = name_to_uuid(&uuid, -1, "*"); if( rc==1 ){ if( validate16(zName, nLen) ){ if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ tktview_page(); return; } if( db_exists("SELECT 1 FROM tag" " WHERE tagname GLOB 'event-%q*'", zName) ){ event_page(); return; } } style_header("No Such Object"); @ <p>No such object: %h(zName)</p> if( nLen<4 ){ @ <p>Object name should be no less than 4 characters. Ten or more @ characters are recommended.</p> } style_footer(); return; }else if( rc==2 ){ cgi_set_parameter("src","info"); ambiguous_page(); return; } zName = blob_str(&uuid); rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); if( rid==0 ){ style_header("Broken Link"); @ <p>No such object: %h(zName)</p> style_footer(); return; } if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
︙ | ︙ | |||
1966 1967 1968 1969 1970 1971 1972 | @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" @ checked="checked" onclick="gebi('%s(zIdCustom)').select();" /> } @ %h(aColor[i].zCName)</label> @ <input type="text" name="%s(zIdCustom)" @ id="%s(zIdCustom)" class="checkinUserColor" @ value="%h(stdClrFound?"":zDefaultColor)" | | > > | 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 | @ <input type="radio" name="%s(zId)" value="%h(aColor[nColor].zColor)" @ checked="checked" onclick="gebi('%s(zIdCustom)').select();" /> } @ %h(aColor[i].zCName)</label> @ <input type="text" name="%s(zIdCustom)" @ id="%s(zIdCustom)" class="checkinUserColor" @ value="%h(stdClrFound?"":zDefaultColor)" @ onfocus="this.form.elements['%s(zId)'][%d(nColor)].checked = true;" @ onload="this.blur();" @ onblur="this.parentElement.style.backgroundColor = this.value ? ('#'+this.value.replace('#','')) : '';" /> @ </td> @ </tr> @ </table> } /* ** Do a comment comparison. |
︙ | ︙ | |||
2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 | const char *zColor; const char *zNewColor; const char *zNewTagFlag; const char *zNewTag; const char *zNewBrFlag; const char *zNewBranch; const char *zCloseFlag; int fPropagateColor; /* True if color propagates before edit */ int fNewPropagateColor; /* True if color propagates after edit */ const char *zChngTime = 0; /* Value of chngtime= query param, if any */ char *zUuid; Blob comment; Stmt q; login_check_credentials(); | > > > > | | 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 | const char *zColor; const char *zNewColor; const char *zNewTagFlag; const char *zNewTag; const char *zNewBrFlag; const char *zNewBranch; const char *zCloseFlag; const char *zHideFlag; int fPropagateColor; /* True if color propagates before edit */ int fNewPropagateColor; /* True if color propagates after edit */ int fHasHidden = 0; /* True if hidden tag already set */ int fHasClosed = 0; /* True if closed tag already set */ const char *zChngTime = 0; /* Value of chngtime= query param, if any */ char *zUuid; Blob comment; char *zBranchName = 0; Stmt q; login_check_credentials(); if( !g.perm.Write ){ login_needed(g.anon.Write); return; } rid = name_to_typed_rid(P("r"), "ci"); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); zComment = db_text(0, "SELECT coalesce(ecomment,comment)" " FROM event WHERE objid=%d", rid); if( zComment==0 ) fossil_redirect_home(); if( P("cancel") ){ cgi_redirectf("ci?name=%s", zUuid); |
︙ | ︙ | |||
2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 | rid, TAG_BGCOLOR)==2; fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor; zNewTagFlag = P("newtag") ? " checked" : ""; zNewTag = PDT("tagname",""); zNewBrFlag = P("newbr") ? " checked" : ""; zNewBranch = PDT("brname",""); zCloseFlag = P("close") ? " checked" : ""; if( P("apply") ){ Blob ctrl; char *zNow; int nChng = 0; login_verify_csrf_secret(); blob_zero(&ctrl); | > | 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 | rid, TAG_BGCOLOR)==2; fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor; zNewTagFlag = P("newtag") ? " checked" : ""; zNewTag = PDT("tagname",""); zNewBrFlag = P("newbr") ? " checked" : ""; zNewBranch = PDT("brname",""); zCloseFlag = P("close") ? " checked" : ""; zHideFlag = P("hide") ? " checked" : ""; if( P("apply") ){ Blob ctrl; char *zNow; int nChng = 0; login_verify_csrf_secret(); blob_zero(&ctrl); |
︙ | ︙ | |||
2121 2122 2123 2124 2125 2126 2127 2128 | char zLabel[30]; sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid); if( P(zLabel) ){ db_multi_exec("REPLACE INTO newtags VALUES(%Q,'-',NULL)", zTag); } } db_finalize(&q); if( zCloseFlag[0] ){ | > > > | > | 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 | char zLabel[30]; sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid); if( P(zLabel) ){ db_multi_exec("REPLACE INTO newtags VALUES(%Q,'-',NULL)", zTag); } } db_finalize(&q); if( zHideFlag[0] ){ db_multi_exec("REPLACE INTO newtags VALUES('hidden','*',NULL)"); } if( zCloseFlag[0] ){ db_multi_exec("REPLACE INTO newtags VALUES('closed','%s',NULL)", is_a_leaf(rid)?"+":"*"); } if( zNewTagFlag[0] && zNewTag[0] ){ db_multi_exec("REPLACE INTO newtags VALUES('sym-%q','+',NULL)", zNewTag); } if( zNewBrFlag[0] && zNewBranch[0] ){ db_multi_exec( "REPLACE INTO newtags " |
︙ | ︙ | |||
2157 2158 2159 2160 2161 2162 2163 | blob_appendf(&ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid); } } db_finalize(&q); if( nChng>0 ){ int nrid; Blob cksum; | | | > > > > > > > > > > > > > > > > > > > > > | | 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 | blob_appendf(&ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid); } } db_finalize(&q); if( nChng>0 ){ int nrid; Blob cksum; blob_appendf(&ctrl, "U %F\n", login_name()); md5sum_blob(&ctrl, &cksum); blob_appendf(&ctrl, "Z %b\n", &cksum); db_begin_transaction(); g.markPrivate = content_is_private(rid); nrid = content_put(&ctrl); manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); assert( blob_is_reset(&ctrl) ); db_end_transaction(0); } cgi_redirectf("ci?name=%s", zUuid); } blob_zero(&comment); blob_append(&comment, zNewComment, -1); zUuid[10] = 0; style_header("Edit Check-in [%s]", zUuid); /* ** chgcbn/chgbn: Handle change of (checkbox for) branch name in ** remaining of form. */ @ <script> @ function chgcbn(checked, branch){ @ val = gebi('brname').value.trim(); @ if( !val || !checked ) val = branch; @ if( checked ) gebi('brname').select(); @ gebi('hbranch').textContent = val; @ cidbrid = document.getElementById('cbranch'); @ if( cidbrid ) cidbrid.textContent = val; @ } @ function chgbn(val, branch){ @ if( !val ) val = branch; @ gebi('newbr').checked = (val!=branch); @ gebi('hbranch').textContent = val; @ cidbrid = document.getElementById('cbranch'); @ if( cidbrid ) cidbrid.textContent = val; @ } @ </script> if( P("preview") ){ Blob suffix; int nTag = 0; @ <b>Preview:</b> @ <blockquote> @ <table border=0> if( zNewColor && zNewColor[0] ){ @ <tr><td style="background-color: %h(zNewColor);"> }else{ @ <tr><td> } @ %!W(blob_str(&comment)) blob_zero(&suffix); blob_appendf(&suffix, "(user: %h", zNewUser); db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag" " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d" " AND tagtype>1 AND tag.tagid=tagxref.tagid", rid); while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
2213 2214 2215 2216 2217 2218 2219 | @ will be overridden as: %s(date_in_standard_format(zChngTime))</p> } @ </blockquote> @ <hr /> blob_reset(&suffix); } @ <p>Make changes to attributes of check-in | | | | 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 | @ will be overridden as: %s(date_in_standard_format(zChngTime))</p> } @ </blockquote> @ <hr /> blob_reset(&suffix); } @ <p>Make changes to attributes of check-in @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p> form_begin(0, "%R/ci_edit"); login_insert_csrf_secret(); @ <div><input type="hidden" name="r" value="%s(zUuid)" /> @ <table border="0" cellspacing="10"> @ <tr><th align="right" valign="top">User:</th> @ <td valign="top"> @ <input type="text" name="u" size="20" value="%h(zNewUser)" /> @ </td></tr> |
︙ | ︙ | |||
2252 2253 2254 2255 2256 2257 2258 2259 | @ <tr><th align="right" valign="top">Tags:</th> @ <td valign="top"> @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) /> @ Add the following new tag name to this check-in:</label> @ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)" @ onkeyup="gebi('newtag').checked=!!this.value" /> db_prepare(&q, | > > > | > > > > > > > > > > > > | | | > > > > > > | > | > | > > > > > > > | | > | < < < < | | | | | | > > > > > > > > | > > | 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 | @ <tr><th align="right" valign="top">Tags:</th> @ <td valign="top"> @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag) /> @ Add the following new tag name to this check-in:</label> @ <input type="text" style="width:15;" name="tagname" value="%h(zNewTag)" @ onkeyup="gebi('newtag').checked=!!this.value" /> zBranchName = db_text(0, "SELECT value FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" " AND tagxref.tagid=%d", rid, TAG_BRANCH); db_prepare(&q, "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)" " ELSE tagname END /*sort*/", rid ); while( db_step(&q)==SQLITE_ROW ){ int tagid = db_column_int(&q, 0); const char *zTagName = db_column_text(&q, 1); int isSpecialTag = fossil_strncmp(zTagName, "sym-", 4)!=0; char zLabel[30]; if( tagid == TAG_CLOSED ){ fHasClosed = 1; }else if( (tagid == TAG_COMMENT) || (tagid == TAG_BRANCH) ){ continue; }else if( tagid==TAG_HIDDEN ){ fHasHidden = 1; }else if( !isSpecialTag && zTagName && fossil_strcmp(&zTagName[4], zBranchName)==0){ continue; } sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid); @ <br /><label> if( P(zLabel) ){ @ <input type="checkbox" name="c%d(tagid)" checked="checked" /> }else{ @ <input type="checkbox" name="c%d(tagid)" /> } if( isSpecialTag ){ @ Cancel special tag <b>%h(zTagName)</b></label> }else{ @ Cancel tag <b>%h(&zTagName[4])</b></label> } } db_finalize(&q); @ </td></tr> if( !zBranchName ){ zBranchName = db_get("main-branch", "trunk"); } if( !zNewBranch || !zNewBranch[0]){ zNewBranch = zBranchName; } @ <tr><th align="right" valign="top">Branching:</th> @ <td valign="top"> @ <label><input id="newbr" type="checkbox" name="newbr"%s(zNewBrFlag) @ onchange="chgcbn(this.checked,'%h(zBranchName)')" /> @ Make this check-in the start of a new branch named:</label> @ <input id="brname" type="text" style="width:15;" name="brname" @ value="%h(zNewBranch)" @ onkeyup="chgbn(this.value.trim(),'%h(zBranchName)')" /></td></tr> if( !fHasHidden ){ @ <tr><th align="right" valign="top">Branch Hiding:</th> @ <td valign="top"> @ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag) /> @ Hide branch @ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span> @ from the timeline starting from this check-in</label> @ </td></tr> } if( !fHasClosed ){ if( is_a_leaf(rid) ){ @ <tr><th align="right" valign="top">Leaf Closure:</th> @ <td valign="top"> @ <label><input type="checkbox" name="close"%s(zCloseFlag) /> @ Mark this leaf as "closed" so that it no longer appears on the @ "leaves" page and is no longer labeled as a "<b>Leaf</b>"</label> @ </td></tr> }else if( zBranchName ){ @ <tr><th align="right" valign="top">Branch Closure:</th> @ <td valign="top"> @ <label><input type="checkbox" name="close"%s(zCloseFlag) /> @ Mark branch @ <span style="font-weight:bold" id="cbranch">%h(zBranchName)</span> @ as "closed".</label> @ </td></tr> } } if( zBranchName ) fossil_free(zBranchName); @ <tr><td colspan="2"> @ <input type="submit" name="preview" value="Preview" /> @ <input type="submit" name="apply" value="Apply Changes" /> @ <input type="submit" name="cancel" value="Cancel" /> @ </td></tr> @ </table> @ </div></form> style_footer(); } |
Changes to src/json.c.
︙ | ︙ | |||
16 17 18 19 20 21 22 | ** ******************************************************************************* ** ** Code for the JSON API. ** ** For notes regarding the public JSON interface, please see: ** | | | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | ** ******************************************************************************* ** ** Code for the JSON API. ** ** For notes regarding the public JSON interface, please see: ** ** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/view ** ** ** Notes for hackers... ** ** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or ** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions then ** dispatch to a JSON-mode-specific command/page handler with the type fossil_json_f(). ** See the API docs for that typedef (below) for the semantics of the callbacks. ** ** */ #include "VERSION.h" #include "config.h" #include "json.h" #include <assert.h> #include <time.h> #if INTERFACE #include "json_detail.h" /* workaround for apparent enum limitation in makeheaders */ #endif |
︙ | ︙ | |||
55 56 57 58 59 60 61 | }; /* ** Returns true (non-0) if fossil appears to be running in JSON mode. */ | | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | }; /* ** Returns true (non-0) if fossil appears to be running in JSON mode. */ int fossil_has_json(){ return g.json.isJsonMode && (g.isHTTP || g.json.post.o); } /* ** Placeholder /json/XXX page impl for NYI (Not Yet Implemented) ** (but planned) pages/commands. */ |
︙ | ︙ | |||
382 383 384 385 386 387 388 | ** ** If an entry is found, this function guarantees that it will return ** either 0 or 1, as opposed to "0 or non-zero", so that clients can ** pass a different value as dflt. Thus they can use, e.g. -1 to know ** whether or not this function found a match (it will return -1 in ** that case). */ | | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | ** ** If an entry is found, this function guarantees that it will return ** either 0 or 1, as opposed to "0 or non-zero", so that clients can ** pass a different value as dflt. Thus they can use, e.g. -1 to know ** whether or not this function found a match (it will return -1 in ** that case). */ int json_getenv_bool(char const * pKey, int dflt ){ cson_value const * v = json_getenv(pKey); const cson_type_id type = v ? cson_value_type_id(v) : CSON_TYPE_UNDEF; switch(type){ case CSON_TYPE_INTEGER: case CSON_TYPE_DOUBLE: return cson_value_get_integer(v) ? 1 : 0; case CSON_TYPE_STRING: { |
︙ | ︙ | |||
473 474 475 476 477 478 479 | return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1); } /* ** The boolean equivalent of json_find_option_cstr(). ** If the option is not found, dftl is returned. */ | | | | | | | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 | return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1); } /* ** The boolean equivalent of json_find_option_cstr(). ** If the option is not found, dftl is returned. */ int json_find_option_bool(char const * zKey, char const * zCLILong, char const * zCLIShort, char dflt ){ int rc = -1; if(!g.isHTTP){ if(NULL != find_option(zCLILong ? zCLILong : zKey, zCLIShort, 0)){ rc = 1; } } if((-1==rc) && fossil_has_json()){ |
︙ | ︙ | |||
985 986 987 988 989 990 991 | FILE * inFile = NULL; char const * jfile = find_option("json-input",NULL,1); if(!jfile || !*jfile){ break; } inFile = (0==strcmp("-",jfile)) ? stdin | | | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 | FILE * inFile = NULL; char const * jfile = find_option("json-input",NULL,1); if(!jfile || !*jfile){ break; } inFile = (0==strcmp("-",jfile)) ? stdin : fossil_fopen(jfile,"rb"); if(!inFile){ g.json.resultCode = FSL_JSON_E_FILE_OPEN_FAILED; fossil_fatal("Could not open JSON file [%s].",jfile) /* Does not return. */ ; } cgi_parse_POST_JSON(inFile, 0); |
︙ | ︙ | |||
1273 1274 1275 1276 1277 1278 1279 | INT(g, xferPanic); INT(g, fullHttpReply); INT(g, xlinkClusterOnly); INT(g, fTimeFormat); INT(g, markPrivate); INT(g, clockSkewSeen); INT(g, isHTTP); | | | | | | | | | | | | | | | | 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 | INT(g, xferPanic); INT(g, fullHttpReply); INT(g, xlinkClusterOnly); INT(g, fTimeFormat); INT(g, markPrivate); INT(g, clockSkewSeen); INT(g, isHTTP); INT(g.url, isFile); INT(g.url, isHttps); INT(g.url, isSsh); INT(g.url, port); INT(g.url, dfltPort); INT(g, useLocalauth); INT(g, noPswd); INT(g, userUid); INT(g, rcvid); INT(g, okCsrf); INT(g, thTrace); INT(g, isHome); INT(g, nAux); INT(g, allowSymlinks); CSTR(g, zMainDbType); CSTR(g, zConfigDbType); CSTR(g, zLocalRoot); CSTR(g, zPath); CSTR(g, zExtra); CSTR(g, zBaseURL); CSTR(g, zTop); CSTR(g, zContentType); CSTR(g, zErrMsg); CSTR(g.url, name); CSTR(g.url, hostname); CSTR(g.url, protocol); CSTR(g.url, path); CSTR(g.url, user); CSTR(g.url, passwd); CSTR(g.url, canonical); CSTR(g.url, proxyAuth); CSTR(g.url, fossil); CSTR(g, zLogin); CSTR(g, zSSLIdentity); CSTR(g, zIpAddr); CSTR(g, zNonce); CSTR(g, zCsrfToken); o = cson_new_object(); |
︙ | ︙ | |||
1663 1664 1665 1666 1667 1668 1669 | ** to simplify the trivial use-cases (which don't need a Blob). */ cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt, char resetBlob){ Stmt q = empty_Stmt; cson_value * pay = NULL; assert( blob_size(pSql) > 0 ); | | | 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 | ** to simplify the trivial use-cases (which don't need a Blob). */ cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt, char resetBlob){ Stmt q = empty_Stmt; cson_value * pay = NULL; assert( blob_size(pSql) > 0 ); db_prepare(&q, "%s", blob_str(pSql) /*safe-for-%s*/); if(resetBlob){ blob_reset(pSql); } pay = json_stmt_to_array_of_obj(&q, pTgt); db_finalize(&q); return pay; |
︙ | ︙ | |||
1978 1979 1980 1981 1982 1983 1984 | SETBUF(jo, "projectCode"); cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); jv2 = cson_value_new_object(); jo2 = cson_value_get_object(jv2); cson_object_set(jo, "sqlite", jv2); sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)", | | | | | | | | 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 | SETBUF(jo, "projectCode"); cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME))); jv2 = cson_value_new_object(); jo2 = cson_value_get_object(jv2); cson_object_set(jo, "sqlite", jv2); sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)", sqlite3_sourceid(), &sqlite3_sourceid()[20], sqlite3_libversion()); SETBUF(jo2, "version"); zDb = db_name("repository"); cson_object_set(jo2, "pageCount", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA \"%w\".page_count", zDb))); cson_object_set(jo2, "pageSize", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA \"%w\".page_size", zDb))); cson_object_set(jo2, "freeList", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA \"%w\".freelist_count", zDb))); sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA \"%w\".encoding", zDb)); SETBUF(jo2, "encoding"); sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA \"%w\".journal_mode", zDb)); cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null()); return jv; #undef SETBUF } |
︙ | ︙ | |||
2014 2015 2016 2017 2018 2019 2020 | Blob * pOut, int filterByMode){ int i = 0; for( ; zPages->name; ++zPages, ++i ){ if(filterByMode){ if(g.isHTTP && zPages->runMode < 0) continue; else if(zPages->runMode > 0) continue; } | | | 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 | Blob * pOut, int filterByMode){ int i = 0; for( ; zPages->name; ++zPages, ++i ){ if(filterByMode){ if(g.isHTTP && zPages->runMode < 0) continue; else if(zPages->runMode > 0) continue; } blob_append(pOut, zPages->name, -1); if((zPages+1)->name){ blob_append(pOut, ", ",2); } } return i; } |
︙ | ︙ |
Changes to src/json_artifact.c.
︙ | ︙ | |||
77 78 79 80 81 82 83 | } db_finalize(&q); return cson_array_value(pParents); } /* ** Generates an artifact Object for the given rid, | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | } db_finalize(&q); return cson_array_value(pParents); } /* ** Generates an artifact Object for the given rid, ** which must refer to a Check-in. ** ** Returned value is NULL or an Object owned by the caller. */ cson_value * json_artifact_for_ci( int rid, char showFiles ){ cson_value * v = NULL; Stmt q = empty_Stmt; static cson_value * eventTypeLabel = NULL; |
︙ | ︙ | |||
205 206 207 208 209 210 211 | cson_object_set(pay, "user", json_new_string(pTktChng->zUser)); cson_object_set(pay, "timestamp", json_julian_to_timestamp(pTktChng->rDate)); manifest_destroy(pTktChng); return cson_object_value(pay); } /* | | | | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | cson_object_set(pay, "user", json_new_string(pTktChng->zUser)); cson_object_set(pay, "timestamp", json_julian_to_timestamp(pTktChng->rDate)); manifest_destroy(pTktChng); return cson_object_value(pay); } /* ** Sub-impl of /json/artifact for check-ins. */ static cson_value * json_artifact_ci( cson_object * zParent, int rid ){ if(!g.perm.Read){ json_set_err( FSL_JSON_E_DENIED, "Viewing check-ins requires 'o' privileges." ); return NULL; }else{ cson_value * artV = json_artifact_for_ci(rid, 1); cson_object * art = cson_value_get_object(artV); if(art){ cson_object_merge( zParent, art, CSON_MERGE_REPLACE ); cson_free_object(art); |
︙ | ︙ | |||
243 244 245 246 247 248 249 | ** Internal helper which returns: ** ** If the "format" (CLI: -f) flag is set function returns the same as ** json_wiki_get_content_format_flag(), else it returns true (non-0) ** if either the includeContent (HTTP) or -content|-c boolean flags ** (CLI) are set. */ | | | | | | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | ** Internal helper which returns: ** ** If the "format" (CLI: -f) flag is set function returns the same as ** json_wiki_get_content_format_flag(), else it returns true (non-0) ** if either the includeContent (HTTP) or -content|-c boolean flags ** (CLI) are set. */ static int json_artifact_get_content_format_flag(){ enum { MagicValue = -9 }; int contentFormat = json_wiki_get_content_format_flag(MagicValue); if(MagicValue == contentFormat){ contentFormat = json_find_option_bool("includeContent","content","c",0) /* deprecated */ ? -1 : 0; } return contentFormat; } extern int json_wiki_get_content_format_flag( int defaultValue ) /* json_wiki.c */; cson_value * json_artifact_wiki(cson_object * zParent, int rid){ if( ! g.perm.RdWiki ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'j' privileges."); return NULL; }else{ enum { MagicValue = -9 }; int const contentFormat = json_artifact_get_content_format_flag(); return json_get_wiki_page_by_rid(rid, contentFormat); } } /* ** Internal helper for routines which add a "status" flag to file ** artifact data. isNew and isDel should be the "is this object new?" |
︙ | ︙ | |||
287 288 289 290 291 292 293 | : "modified"); } cson_value * json_artifact_file(cson_object * zParent, int rid){ cson_object * pay = NULL; Stmt q = empty_Stmt; cson_array * checkin_arr = NULL; | | | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | : "modified"); } cson_value * json_artifact_file(cson_object * zParent, int rid){ cson_object * pay = NULL; Stmt q = empty_Stmt; cson_array * checkin_arr = NULL; int contentFormat; i64 contentSize = -1; char * parentUuid; if( ! g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' privileges."); return NULL; } |
︙ | ︙ | |||
344 345 346 347 348 349 350 | rid ); if(parentUuid){ cson_object_set( zParent, "parent", json_new_string(parentUuid) ); fossil_free(parentUuid); } | | | | | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | rid ); if(parentUuid){ cson_object_set( zParent, "parent", json_new_string(parentUuid) ); fossil_free(parentUuid); } /* Find check-ins associated with this file... */ db_prepare(&q, "SELECT filename.name AS name, " " (mlink.pid==0) AS isNew," " (mlink.fid==0) AS isDel," " cast(strftime('%%s',event.mtime) as int) AS timestamp," " coalesce(event.ecomment,event.comment) as comment," " coalesce(event.euser,event.user) as user," #if 0 " a.size AS size," /* same for all check-ins. */ #endif " b.uuid as checkin, " #if 0 " mlink.mperm as mperm," #endif " coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND " " rid=mlink.mid),'trunk') as branch" " FROM mlink, filename, event, blob a, blob b" " WHERE filename.fnid=mlink.fnid" " AND event.objid=mlink.mid" " AND a.rid=mlink.fid" " AND b.rid=mlink.mid" " AND mlink.fid=%d" " ORDER BY filename.name, event.mtime", TAG_BRANCH, rid ); /* TODO: add a "state" flag for the file in each check-in, e.g. "modified", "new", "deleted". */ checkin_arr = cson_new_array(); cson_object_set(pay, "checkins", cson_array_value(checkin_arr)); while( (SQLITE_ROW==db_step(&q) ) ){ cson_object * row = cson_value_get_object(cson_sqlite3_row_to_object(q.pStmt)); /* FIXME: move this isNew/isDel stuff into an SQL CASE statement. */ |
︙ | ︙ |
Changes to src/json_branch.c.
︙ | ︙ | |||
64 65 66 67 68 69 70 | */ static cson_value * json_branch_list(){ cson_value * payV; cson_object * pay; cson_value * listV; cson_array * list; char const * range = NULL; | | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | */ static cson_value * json_branch_list(){ cson_value * payV; cson_object * pay; cson_value * listV; cson_array * list; char const * range = NULL; int branchListFlags = BRL_OPEN_ONLY; char * sawConversionError = NULL; Stmt q; if( !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); return NULL; } |
︙ | ︙ | |||
100 101 102 103 104 105 106 | if(!range || !*range){ range = "o"; } /* Normalize range values... */ switch(*range){ case 'c': range = "closed"; | | | | | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | if(!range || !*range){ range = "o"; } /* Normalize range values... */ switch(*range){ case 'c': range = "closed"; branchListFlags = BRL_CLOSED_ONLY; break; case 'a': range = "all"; branchListFlags = BRL_BOTH; break; default: range = "open"; branchListFlags = BRL_OPEN_ONLY; break; }; cson_object_set(pay,"range",json_new_string(range)); if( g.localOpen ){ /* add "current" property (branch name). */ int vid = db_lget_int("checkout", 0); char const * zCurrent = vid ? db_text(0, "SELECT value FROM tagxref" " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH) : 0; if(zCurrent){ cson_object_set(pay,"current",json_new_string(zCurrent)); } } branch_prepare_list_query(&q, branchListFlags); cson_object_set(pay,"branches",listV); while((SQLITE_ROW==db_step(&q))){ cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0); if(v){ cson_array_append(list,v); }else if(!sawConversionError){ sawConversionError = mprintf("Column-to-json failed @ %s:%d", |
︙ | ︙ | |||
289 290 291 292 293 294 295 | blob_appendf(&branch, "Z %b\n", &mcksum); brid = content_put(&branch); if( brid==0 ){ fossil_fatal("Problem committing manifest: %s", g.zErrMsg); } db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); | | | | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | blob_appendf(&branch, "Z %b\n", &mcksum); brid = content_put(&branch); if( brid==0 ){ fossil_fatal("Problem committing manifest: %s", g.zErrMsg); } db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ fossil_fatal("%s\n", g.zErrMsg); } assert( blob_is_reset(&branch) ); content_deltify(rootid, brid, 0); if( zNewRid ){ *zNewRid = brid; } |
︙ | ︙ |
Changes to src/json_config.c.
︙ | ︙ | |||
51 52 53 54 55 56 57 | ** mostly a copy of the config options in configure.c, but that data ** is private and cannot be re-used directly here. */ static const struct JsonConfigProperty { char const * name; int groupMask; } JsonConfigProperties[] = { | | > > | > > > > > > > > < > > | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | ** mostly a copy of the config options in configure.c, but that data ** is private and cannot be re-used directly here. */ static const struct JsonConfigProperty { char const * name; int groupMask; } JsonConfigProperties[] = { { "css", CONFIGSET_CSS }, { "header", CONFIGSET_SKIN }, { "footer", CONFIGSET_SKIN }, { "details", CONFIGSET_SKIN }, { "logo-mimetype", CONFIGSET_SKIN }, { "logo-image", CONFIGSET_SKIN }, { "background-mimetype", CONFIGSET_SKIN }, { "background-image", CONFIGSET_SKIN }, { "timeline-block-markup", CONFIGSET_SKIN }, { "timeline-max-comment", CONFIGSET_SKIN }, { "timeline-plaintext", CONFIGSET_SKIN }, { "adunit", CONFIGSET_SKIN }, { "adunit-omit-if-admin", CONFIGSET_SKIN }, { "adunit-omit-if-user", CONFIGSET_SKIN }, { "project-name", CONFIGSET_PROJ }, { "short-project-name", CONFIGSET_PROJ }, { "project-description", CONFIGSET_PROJ }, { "index-page", CONFIGSET_PROJ }, { "manifest", CONFIGSET_PROJ }, { "binary-glob", CONFIGSET_PROJ }, { "clean-glob", CONFIGSET_PROJ }, { "ignore-glob", CONFIGSET_PROJ }, { "keep-glob", CONFIGSET_PROJ }, { "crnl-glob", CONFIGSET_PROJ }, { "encoding-glob", CONFIGSET_PROJ }, { "empty-dirs", CONFIGSET_PROJ }, { "allow-symlinks", CONFIGSET_PROJ }, { "dotfiles", CONFIGSET_PROJ }, { "ticket-table", CONFIGSET_TKT }, { "ticket-common", CONFIGSET_TKT }, { "ticket-change", CONFIGSET_TKT }, { "ticket-newpage", CONFIGSET_TKT }, { "ticket-viewpage", CONFIGSET_TKT }, { "ticket-editpage", CONFIGSET_TKT }, |
︙ | ︙ | |||
111 112 113 114 115 116 117 | zName = json_command_arg(i); for( ; zName; zName = json_command_arg(++i) ){ if(0==(strcmp("all", zName))){ confMask = CONFIGSET_ALL; }else if(0==(strcmp("project", zName))){ confMask |= CONFIGSET_PROJ; }else if(0==(strcmp("skin", zName))){ | | | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | zName = json_command_arg(i); for( ; zName; zName = json_command_arg(++i) ){ if(0==(strcmp("all", zName))){ confMask = CONFIGSET_ALL; }else if(0==(strcmp("project", zName))){ confMask |= CONFIGSET_PROJ; }else if(0==(strcmp("skin", zName))){ confMask |= (CONFIGSET_CSS|CONFIGSET_SKIN); }else if(0==(strcmp("ticket", zName))){ confMask |= CONFIGSET_TKT; }else if(0==(strcmp("skin-backup", zName))){ optSkinBackups = 1; }else{ json_set_err( FSL_JSON_E_INVALID_ARGS, "Unknown config area: %s", zName); |
︙ | ︙ | |||
138 139 140 141 142 143 144 | const struct JsonConfigProperty * prop = &JsonConfigProperties[0]; blob_append(&sql," OR name IN (",-1); for( i = 0; prop->name; ++prop ){ if(prop->groupMask & confMask){ if( i++ ){ blob_append(&sql,",",1); } | | | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | const struct JsonConfigProperty * prop = &JsonConfigProperties[0]; blob_append(&sql," OR name IN (",-1); for( i = 0; prop->name; ++prop ){ if(prop->groupMask & confMask){ if( i++ ){ blob_append(&sql,",",1); } blob_append_sql(&sql, "%Q", prop->name); } } blob_append(&sql,") ", -1); } if( optSkinBackups ){ blob_append(&sql, " OR name GLOB 'skin:*'", -1); } blob_append(&sql," ORDER BY name", -1); db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); pay = cson_new_object(); while( (SQLITE_ROW==db_step(&q)) ){ cson_object_set(pay, db_column_text(&q,0), json_new_string(db_column_text(&q,1))); } |
︙ | ︙ |
Changes to src/json_detail.h.
︙ | ︙ | |||
255 256 257 258 259 260 261 | ** b) We are running in JSON CLI mode, but no POST data has been fed ** in. ** ** Whether or not we need to take args from CLI or POST data makes a ** difference in argument/parameter handling in many JSON routines, ** and thus this distinction. */ | | | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | ** b) We are running in JSON CLI mode, but no POST data has been fed ** in. ** ** Whether or not we need to take args from CLI or POST data makes a ** difference in argument/parameter handling in many JSON routines, ** and thus this distinction. */ int fossil_has_json(); enum json_get_changed_files_flags { json_get_changed_files_ELIDE_PARENT = 1 << 0 }; #endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/ #endif /* FOSSIL_ENABLE_JSON */ |
Changes to src/json_dir.c.
︙ | ︙ | |||
80 81 82 83 84 85 86 | */ if( zCI && *zCI ){ pM = manifest_get_by_name(zCI, &rid); if( pM ){ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); }else{ json_set_err(FSL_JSON_E_UNRESOLVED_UUID, | | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | */ if( zCI && *zCI ){ pM = manifest_get_by_name(zCI, &rid); if( pM ){ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); }else{ json_set_err(FSL_JSON_E_UNRESOLVED_UUID, "Check-in name [%s] is unresolved.", zCI); return NULL; } } /* Jump through some hoops to find the directory name... */ zDX = json_find_option_cstr("name",NULL,NULL); |
︙ | ︙ |
Changes to src/json_finfo.c.
︙ | ︙ | |||
60 61 62 63 64 65 66 | } zBefore = json_find_option_cstr("before",NULL,"b"); zAfter = json_find_option_cstr("after",NULL,"a"); limit = json_find_option_int("limit",NULL,"n", -1); zCheckin = json_find_option_cstr("checkin",NULL,"ci"); | | | | | | | | | < | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | } zBefore = json_find_option_cstr("before",NULL,"b"); zAfter = json_find_option_cstr("after",NULL,"a"); limit = json_find_option_int("limit",NULL,"n", -1); zCheckin = json_find_option_cstr("checkin",NULL,"ci"); blob_append_sql(&sql, /*0*/ "SELECT b.uuid," /*1*/ " ci.uuid," /*2*/ " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* Current file uuid */ /*3*/ " cast(strftime('%%s',event.mtime) AS INTEGER)," /*4*/ " coalesce(event.euser, event.user)," /*5*/ " coalesce(event.ecomment, event.comment)," /*6*/ " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */ /*7*/ " event.bgcolor," /*8*/ " b.size," /*9*/ " (mlink.pid==0) AS isNew," /*10*/ " (mlink.fid==0) AS isDel" " FROM mlink, blob b, event, blob ci, filename" " WHERE filename.name=%Q" " AND mlink.fnid=filename.fnid" " AND b.rid=mlink.fid" " AND event.objid=mlink.mid" " AND event.objid=ci.rid", zFilename ); if( zCheckin && *zCheckin ){ char * zU = NULL; int rc = name_to_uuid2( zCheckin, "ci", &zU ); /*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/ if(rc<=0){ json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID : FSL_JSON_E_RESOURCE_NOT_FOUND, "Check-in UUID %s.", (rc<0) ? "is ambiguous" : "not found"); blob_reset(&sql); return NULL; } blob_append_sql(&sql, " AND ci.uuid='%q'", zU); free(zU); }else{ if( zAfter && *zAfter ){ blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zAfter); sort = 1; }else if( zBefore && *zBefore ){ blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zBefore); } } blob_append_sql(&sql," ORDER BY event.mtime %s /*sort*/", (sort>0?"ASC":"DESC")); /*printf("SQL=\n%s\n",blob_str(&sql));*/ db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); pay = cson_new_object(); cson_object_set(pay, "name", json_new_string(zFilename)); if( limit > 0 ){ cson_object_set(pay, "limit", json_new_int(limit)); } |
︙ | ︙ |
Changes to src/json_query.c.
︙ | ︙ | |||
62 63 64 65 66 67 68 | json_set_err(FSL_JSON_E_MISSING_ARGS, "'sql' (-s) argument is missing."); return NULL; } zFmt = json_find_option_cstr2("format",NULL,"f",3); if(!zFmt) zFmt = "o"; | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | json_set_err(FSL_JSON_E_MISSING_ARGS, "'sql' (-s) argument is missing."); return NULL; } zFmt = json_find_option_cstr2("format",NULL,"f",3); if(!zFmt) zFmt = "o"; db_prepare(&q,"%s", zSql/*safe-for-%s*/); if( 0 == sqlite3_column_count( q.pStmt ) ){ json_set_err(FSL_JSON_E_USAGE, "Input query has no result columns. " "Only SELECT-like queries are supported."); db_finalize(&q); return NULL; } |
︙ | ︙ |
Changes to src/json_report.c.
︙ | ︙ | |||
202 203 204 205 206 207 208 | limit = json_find_option_int("limit",NULL,"n",-1); /* Copy over report's SQL...*/ blob_append(&sql, db_column_text(&q,0), -1); zTitle = mprintf("%s", db_column_text(&q,1)); db_finalize(&q); | | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | limit = json_find_option_int("limit",NULL,"n",-1); /* Copy over report's SQL...*/ blob_append(&sql, db_column_text(&q,0), -1); zTitle = mprintf("%s", db_column_text(&q,1)); db_finalize(&q); db_prepare(&q, "%s", blob_sql_text(&sql)); /** Build the response... */ pay = cson_new_object(); cson_object_set(pay, "report", json_new_int(nReport)); cson_object_set(pay, "title", json_new_string(zTitle)); if(limit>0){ |
︙ | ︙ |
Changes to src/json_status.c.
︙ | ︙ | |||
67 68 69 70 71 72 73 | cson_object_set(oPay, "checkout", cson_object_value(tmpO)); zTmp = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); cson_object_set(tmpO, "uuid", json_new_string(zTmp) ); free(zTmp); cson_object_set( tmpO, "tags", json_tags_for_checkin_rid(vid, 0) ); | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | cson_object_set(oPay, "checkout", cson_object_value(tmpO)); zTmp = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); cson_object_set(tmpO, "uuid", json_new_string(zTmp) ); free(zTmp); cson_object_set( tmpO, "tags", json_tags_for_checkin_rid(vid, 0) ); /* FIXME: optimize the datetime/timestamp queries into 1 query. */ zTmp = db_text(0, "SELECT datetime(mtime) || " "' UTC' FROM event WHERE objid=%d", vid); cson_object_set(tmpO, "datetime", json_new_string(zTmp)); free(zTmp); iMtime = db_int64(0, "SELECT CAST(strftime('%%s',mtime) AS INTEGER) " |
︙ | ︙ | |||
113 114 115 116 117 118 119 | zStatus = "deleted"; }else if( isNew ){ zStatus = "new" /* maintenance reminder: MUST come BEFORE the isChnged checks. */; }else if( isRenamed ){ zStatus = "renamed"; }else if( !file_wd_isfile_or_link(zFullName) ){ | | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | zStatus = "deleted"; }else if( isNew ){ zStatus = "new" /* maintenance reminder: MUST come BEFORE the isChnged checks. */; }else if( isRenamed ){ zStatus = "renamed"; }else if( !file_wd_isfile_or_link(zFullName) ){ if( file_access(zFullName, F_OK)==0 ){ zStatus = "notAFile"; ++nErr; }else{ zStatus = "missing"; ++nErr; } }else if( 2==isChnged ){ |
︙ | ︙ | |||
135 136 137 138 139 140 141 | }else if( 1==isChnged ){ if( file_contains_merge_marker(zFullName) ){ zStatus = "conflict"; }else{ zStatus = "edited"; } } | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | }else if( 1==isChnged ){ if( file_contains_merge_marker(zFullName) ){ zStatus = "conflict"; }else{ zStatus = "edited"; } } oFile = cson_new_object(); cson_array_append( aFiles, cson_object_value(oFile) ); /* optimization potential: move these keys into cson_strings to take advantage of refcounting. */ cson_object_set( oFile, "name", json_new_string( zPathname ) ); cson_object_set( oFile, "status", json_new_string( zStatus ) ); |
︙ | ︙ |
Changes to src/json_tag.c.
︙ | ︙ | |||
299 300 301 302 303 304 305 | " AND event.type GLOB '%q'" " AND blob.rid IN (" " SELECT rid FROM tagxref" " WHERE tagtype>0 AND tagid=%d" " )" " ORDER BY event.mtime DESC" "%s LIMIT %d", | | | 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | " AND event.type GLOB '%q'" " AND blob.rid IN (" " SELECT rid FROM tagxref" " WHERE tagtype>0 AND tagid=%d" " )" " ORDER BY event.mtime DESC" "%s LIMIT %d", zSqlBase /*safe-for-%s*/, zType, tagid, (limit>0)?"":"--", limit ); listV = json_stmt_to_array_of_obj(&q, NULL); db_finalize(&q); } if(!listV) { |
︙ | ︙ | |||
351 352 353 354 355 356 357 | } } payV = cson_value_new_object(); pay = cson_value_get_object(payV); cson_object_set(pay, "raw", cson_value_new_bool(fRaw) ); if( zCheckin ){ /** | | | | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | } } payV = cson_value_new_object(); pay = cson_value_get_object(payV); cson_object_set(pay, "raw", cson_value_new_bool(fRaw) ); if( zCheckin ){ /** Tags for a specific check-in. Output format: RAW mode: { "sym-tagname": (value || null), ...other tags... } Non-raw: { "tagname": (value || null), ...other tags... } */ cson_value * objV = NULL; cson_object * obj = NULL; int const rid = name_to_rid(zCheckin); if(0==rid){ json_set_err(FSL_JSON_E_UNRESOLVED_UUID, "Could not find artifact for check-in [%s].", zCheckin); goto error; } cson_object_set(pay, "checkin", json_new_string(zCheckin)); db_prepare(&q, "SELECT tagname, value FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid" |
︙ | ︙ | |||
440 441 442 443 444 445 446 | -1 ); if(!fTicket){ blob_append(&sql, " AND tagname NOT GLOB('tkt-*') ", -1); } blob_append(&sql, " ORDER BY tagname", -1); | | | 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | -1 ); if(!fTicket){ blob_append(&sql, " AND tagname NOT GLOB('tkt-*') ", -1); } blob_append(&sql, " ORDER BY tagname", -1); db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); cson_object_set(pay, "includeTickets", cson_value_new_bool(fTicket) ); while( SQLITE_ROW == db_step(&q) ){ const char *zName = db_column_text(&q, 0); if(NULL==arV){ arV = cson_value_new_array(); ar = cson_value_get_array(arV); |
︙ | ︙ |
Changes to src/json_timeline.c.
︙ | ︙ | |||
64 65 66 67 68 69 70 | } /* ** Create a temporary table suitable for storing timeline data. */ static void json_timeline_temp_table(void){ /* Field order MUST match that from json_timeline_query()!!! */ | | | | > | | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | } /* ** Create a temporary table suitable for storing timeline data. */ static void json_timeline_temp_table(void){ /* Field order MUST match that from json_timeline_query()!!! */ static const char zSql[] = @ CREATE TEMP TABLE IF NOT EXISTS json_timeline( @ sortId INTEGER PRIMARY KEY, @ rid INTEGER, @ uuid TEXT, @ mtime INTEGER, @ timestampString TEXT, @ comment TEXT, @ user TEXT, @ isLeaf BOOLEAN, @ bgColor TEXT, @ eventType TEXT, @ tags TEXT, @ tagId INTEGER, @ brief TEXT @ ) ; db_multi_exec("%s", zSql /*safe-for-%s*/); } /* ** Return a pointer to a constant string that forms the basis ** for a timeline query for the JSON interface. It MUST NOT ** be used in a formatted string argument. */ char const * json_timeline_query(void){ /* Field order MUST match that from json_timeline_temp_table()!!! */ static const char zBaseSql[] = @ SELECT @ NULL, @ blob.rid, @ uuid, @ CAST(strftime('%s',event.mtime) AS INTEGER), @ datetime(event.mtime), @ coalesce(ecomment, comment), @ coalesce(euser, user), @ blob.rid IN leaf, @ bgcolor, @ event.type, @ (SELECT group_concat(substr(tagname,5), ',') FROM tag, tagxref @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0) as tags, @ tagid as tagId, @ brief as brief @ FROM event JOIN blob @ WHERE blob.rid=event.objid ; return zBaseSql; } /* ** Internal helper to append query information if the |
︙ | ︙ | |||
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | ** both are specified, which one takes precedence is unspecified. */ static char json_timeline_add_tag_branch_clause(Blob *pSql, cson_object * pPayload){ char const * zTag = NULL; char const * zBranch = NULL; char const * zMiOnly = NULL; int tagid = 0; if(! g.perm.Read ){ return 0; } zTag = json_find_option_cstr("tag",NULL,NULL); if(!zTag || !*zTag){ zBranch = json_find_option_cstr("branch",NULL,NULL); if(!zBranch || !*zBranch){ return 0; } zTag = zBranch; zMiOnly = json_find_option_cstr("mionly",NULL,NULL); } tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag); if(tagid<=0){ return -1; } if(pPayload){ cson_object_set( pPayload, zBranch ? "branch" : "tag", json_new_string(zTag) ); } blob_appendf(pSql, " AND (" " EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid); if(zBranch){ /* from "r" flag code in page_timeline().*/ blob_appendf(pSql, " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid" " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)", tagid); if( zMiOnly==0 ){ blob_appendf(pSql, " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid" " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)", tagid); } } blob_append(pSql," ) ",3); return 1; } /* ** Helper for the timeline family of functions. Possibly appends 1 | > > > > > > > > > > > > > > > > > > > > | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | ** both are specified, which one takes precedence is unspecified. */ static char json_timeline_add_tag_branch_clause(Blob *pSql, cson_object * pPayload){ char const * zTag = NULL; char const * zBranch = NULL; char const * zMiOnly = NULL; char const * zUnhide = NULL; int tagid = 0; if(! g.perm.Read ){ return 0; } zTag = json_find_option_cstr("tag",NULL,NULL); if(!zTag || !*zTag){ zBranch = json_find_option_cstr("branch",NULL,NULL); if(!zBranch || !*zBranch){ return 0; } zTag = zBranch; zMiOnly = json_find_option_cstr("mionly",NULL,NULL); } zUnhide = json_find_option_cstr("unhide",NULL,NULL); tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag); if(tagid<=0){ return -1; } if(pPayload){ cson_object_set( pPayload, zBranch ? "branch" : "tag", json_new_string(zTag) ); } blob_appendf(pSql, " AND (" " EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", tagid); if(!zUnhide){ blob_appendf(pSql, " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=blob.rid" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)", TAG_HIDDEN); } if(zBranch){ /* from "r" flag code in page_timeline().*/ blob_appendf(pSql, " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid" " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)", tagid); if( !zUnhide ){ blob_appendf(pSql, " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid" " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)", TAG_HIDDEN); } if( zMiOnly==0 ){ blob_appendf(pSql, " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid" " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)", tagid); if( !zUnhide ){ blob_appendf(pSql, " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid" " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)", TAG_HIDDEN); } } } blob_append(pSql," ) ",3); return 1; } /* ** Helper for the timeline family of functions. Possibly appends 1 |
︙ | ︙ | |||
294 295 296 297 298 299 300 | ** flags may optionally be a bitmask of json_get_changed_files flags, ** or 0 for defaults. */ cson_value * json_get_changed_files(int rid, int flags){ cson_value * rowsV = NULL; cson_array * rows = NULL; Stmt q = empty_Stmt; | | | 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | ** flags may optionally be a bitmask of json_get_changed_files flags, ** or 0 for defaults. */ cson_value * json_get_changed_files(int rid, int flags){ cson_value * rowsV = NULL; cson_array * rows = NULL; Stmt q = empty_Stmt; db_prepare(&q, "SELECT (pid==0) AS isnew," " (fid==0) AS isdel," " (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name," " blob.uuid as uuid," " (SELECT uuid FROM blob WHERE rid=pid) as parent," " blob.size as size" " FROM mlink, blob" |
︙ | ︙ | |||
362 363 364 365 366 367 368 | " coalesce(euser, user) as user," " blob.rid IN leaf as isLeaf," " bgcolor as bgColor" " FROM event JOIN blob" " WHERE blob.rid=event.objid", -1); | | | | | 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | " coalesce(euser, user) as user," " blob.rid IN leaf as isLeaf," " bgcolor as bgColor" " FROM event JOIN blob" " WHERE blob.rid=event.objid", -1); blob_append_sql(&sql, " AND event.type='ci'" " AND blob.rid IN (SELECT rid FROM tagxref" " WHERE tagtype>0 AND tagid=%d AND srcid!=0)" " ORDER BY event.mtime DESC", TAG_BRANCH); limit = json_timeline_limit(20); if(limit>0){ blob_append_sql(&sql," LIMIT %d ",limit); } db_prepare(&q,"%s", blob_sql_text(&sql)); blob_reset(&sql); pay = json_stmt_to_array_of_obj(&q, NULL); db_finalize(&q); assert(NULL != pay); if(pay){ /* get the array-form tags of each record. */ cson_string * tags = cson_new_string("tags",4); |
︙ | ︙ | |||
434 435 436 437 438 439 440 | Stmt q = empty_Stmt; char warnRowToJsonFailed = 0; Blob sql = empty_blob; if( !g.perm.Hyperlink ){ /* Reminder to self: HTML impl requires 'o' (Read) rights. */ | | | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | Stmt q = empty_Stmt; char warnRowToJsonFailed = 0; Blob sql = empty_blob; if( !g.perm.Hyperlink ){ /* Reminder to self: HTML impl requires 'o' (Read) rights. */ json_set_err( FSL_JSON_E_DENIED, "Check-in timeline requires 'h' access." ); return NULL; } verboseFlag = json_find_option_bool("verbose",NULL,"v",0); if( !verboseFlag ){ verboseFlag = json_find_option_bool("files",NULL,"f",0); } payV = cson_value_new_object(); |
︙ | ︙ | |||
460 461 462 463 464 465 466 | } (void)0 #if 0 /* only for testing! */ tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); SET("timelineSql"); #endif | | | 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 | } (void)0 #if 0 /* only for testing! */ tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); SET("timelineSql"); #endif db_multi_exec("%s", blob_buffer(&sql)/*safe-for-%s*/); blob_reset(&sql); db_prepare(&q, "SELECT " " rid AS rid" " FROM json_timeline" " ORDER BY rowid"); listV = cson_value_new_array(); list = cson_value_get_array(listV); |
︙ | ︙ | |||
522 523 524 525 526 527 528 | if(check){ json_set_err(check, "Query initialization failed."); goto error; } #if 0 /* only for testing! */ | | < | | < | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 | if(check){ json_set_err(check, "Query initialization failed."); goto error; } #if 0 /* only for testing! */ cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql)))); #endif db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/); blob_reset(&sql); db_prepare(&q, "SELECT" " uuid AS uuid," " mtime AS timestamp," #if 0 " timestampString AS timestampString," #endif " comment AS comment, " " user AS user," " eventType AS eventType" #if 0 /* can wiki pages have tags? */ " tags AS tags," /*FIXME: split this into a JSON array*/ " tagId AS tagId," #endif " FROM json_timeline" " ORDER BY rowid"); list = cson_new_array(); json_stmt_to_array_of_obj(&q, list); cson_object_set(pay, "timeline", cson_array_value(list)); goto ok; error: assert( 0 != g.json.resultCode ); cson_value_free(payV); |
︙ | ︙ | |||
585 586 587 588 589 590 591 | pay = cson_value_get_object(payV); check = json_timeline_setup_sql( "t", &sql, pay ); if(check){ json_set_err(check, "Query initialization failed."); goto error; } | | | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 | pay = cson_value_get_object(payV); check = json_timeline_setup_sql( "t", &sql, pay ); if(check){ json_set_err(check, "Query initialization failed."); goto error; } db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/); #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ json_set_err((cson_rc.AllocError==check) \ ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN, \ "Object property insertion failed."); \ goto error;\ } (void)0 |
︙ | ︙ | |||
616 617 618 619 620 621 622 | " timestampString AS timestampString," #endif " user AS user," " eventType AS eventType," " comment AS comment," " brief AS briefComment" " FROM json_timeline" | | < | 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 | " timestampString AS timestampString," #endif " user AS user," " eventType AS eventType," " comment AS comment," " brief AS briefComment" " FROM json_timeline" " ORDER BY rowid"); listV = cson_value_new_array(); list = cson_value_get_array(listV); tmp = listV; SET("timeline"); while( (SQLITE_ROW == db_step(&q) )){ /* convert each row into a JSON object...*/ int rc; |
︙ | ︙ |
Changes to src/json_user.c.
︙ | ︙ | |||
284 285 286 287 288 289 290 | } forceLogout = cson_value_true() /* reminders: 1) does not allocate. 2) we do this because changing a name invalidates any login token because the old name is part of the token hash. */; | | | | | | | | | | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | } forceLogout = cson_value_true() /* reminders: 1) does not allocate. 2) we do this because changing a name invalidates any login token because the old name is part of the token hash. */; blob_append_sql(&sql, ", login=%Q", zNameNew); ++gotFields; } } if( zCap && *zCap ){ if(!g.perm.Admin || !g.perm.Setup){ /* we "could" arguably silently ignore cap in this case. */ json_set_err(FSL_JSON_E_DENIED, "Changing capabilities requires 'a' or 's' privileges."); goto error; } blob_append_sql(&sql, ", cap=%Q", zCap); ++gotFields; } if( zPW && *zPW ){ if(!g.perm.Admin && !g.perm.Setup && !g.perm.Password){ json_set_err( FSL_JSON_E_DENIED, "Password change requires 'a', 's', " "or 'p' permissions."); goto error; }else{ #define TRY_LOGIN_GROUP 0 /* login group support is not yet implemented. */ #if !TRY_LOGIN_GROUP char * zPWHash = NULL; ++gotFields; zPWHash = sha1_shared_secret(zPW, zNameNew ? zNameNew : zName, NULL); blob_append_sql(&sql, ", pw=%Q", zPWHash); free(zPWHash); #else ++gotFields; blob_append_sql(&sql, ", pw=coalesce(shared_secret(%Q,%Q," "(SELECT value FROM config WHERE name='project-code')))", zPW, zNameNew ? zNameNew : zName); /* shared_secret() func is undefined? */ #endif } } if( zInfo ){ blob_append_sql(&sql, ", info=%Q", zInfo); ++gotFields; } if((g.perm.Admin || g.perm.Setup) && forceLogout && cson_value_get_bool(forceLogout)){ blob_append(&sql, ", cookie=NULL, cexpire=NULL", -1); ++gotFields; } if(!gotFields){ json_set_err( FSL_JSON_E_MISSING_ARGS, "Required user data are missing."); goto error; } assert(uid>0); #if !TRY_LOGIN_GROUP blob_append_sql(&sql, " WHERE uid=%d", uid); #else /* need name for login group support :/ */ blob_append_sql(&sql, " WHERE login=%Q", zName); #endif #if 0 puts(blob_str(&sql)); cson_output_FILE( cson_object_value(pUser), stdout, NULL ); #endif db_prepare(&q, "%s", blob_sql_text(&sql)); db_exec(&q); db_finalize(&q); #if TRY_LOGIN_GROUP if( zPW || cson_value_get_bool(forceLogout) ){ Blob groupSql = empty_blob; char * zErr = NULL; blob_append_sql(&groupSql, "INSERT INTO user(login)" " SELECT %Q WHERE NOT EXISTS(SELECT 1 FROM user WHERE login=%Q);", zName, zName ); blob_append(&groupSql, blob_str(&sql), blob_size(&sql)); login_group_sql(blob_str(&groupSql), NULL, NULL, &zErr); blob_reset(&groupSql); |
︙ | ︙ |
Changes to src/json_wiki.c.
︙ | ︙ | |||
78 79 80 81 82 83 84 | ** is not returned in the response. If contentFormat is 0 then the ** contentSize reflects the number of bytes, not characters, stored in ** the page. ** ** The returned value, if not NULL, is-a JSON Object owned by the ** caller. If it returns NULL then it may set g.json's error state. */ | | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | ** is not returned in the response. If contentFormat is 0 then the ** contentSize reflects the number of bytes, not characters, stored in ** the page. ** ** The returned value, if not NULL, is-a JSON Object owned by the ** caller. If it returns NULL then it may set g.json's error state. */ cson_value * json_get_wiki_page_by_rid(int rid, int contentFormat){ Manifest * pWiki = NULL; if( NULL == (pWiki = manifest_get(rid, CFTYPE_WIKI, 0)) ){ json_set_err( FSL_JSON_E_UNKNOWN, "Error reading wiki page from manifest (rid=%d).", rid ); return NULL; }else{ |
︙ | ︙ | |||
143 144 145 146 147 148 149 | } /* ** Searches for the latest version of a wiki page with the given ** name. If found it behaves like json_get_wiki_page_by_rid(theRid, ** contentFormat), else it returns NULL. */ | | | 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | } /* ** Searches for the latest version of a wiki page with the given ** name. If found it behaves like json_get_wiki_page_by_rid(theRid, ** contentFormat), else it returns NULL. */ cson_value * json_get_wiki_page_by_name(char const * zPageName, int contentFormat){ int rid; rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x, blob b" " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' " " AND b.rid=x.rid" " ORDER BY x.mtime DESC LIMIT 1", zPageName |
︙ | ︙ | |||
173 174 175 176 177 178 179 | ** [h]tml = 1 ** [n]one = 0 ** [r]aw = -1 ** ** The return value is intended for use with ** json_get_wiki_page_by_rid() and friends. */ | | | | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | ** [h]tml = 1 ** [n]one = 0 ** [r]aw = -1 ** ** The return value is intended for use with ** json_get_wiki_page_by_rid() and friends. */ int json_wiki_get_content_format_flag( int defaultValue ){ int contentFormat = defaultValue; char const * zFormat = json_find_option_cstr("format",NULL,"f"); if( !zFormat || !*zFormat ){ return contentFormat; } else if('r'==*zFormat){ contentFormat = -1; } |
︙ | ︙ | |||
201 202 203 204 205 206 207 | ** non-empty/non-NULL value. zSymname takes precedence. On success ** the result of one of json_get_wiki_page_by_rid() or ** json_get_wiki_page_by_name() will be returned (owned by the ** caller). On error g.json's error state is set and NULL is returned. */ static cson_value * json_wiki_get_by_name_or_symname(char const * zPageName, char const * zSymname, | | | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | ** non-empty/non-NULL value. zSymname takes precedence. On success ** the result of one of json_get_wiki_page_by_rid() or ** json_get_wiki_page_by_name() will be returned (owned by the ** caller). On error g.json's error state is set and NULL is returned. */ static cson_value * json_wiki_get_by_name_or_symname(char const * zPageName, char const * zSymname, int contentFormat ){ if(!zSymname || !*zSymname){ return json_get_wiki_page_by_name(zPageName, contentFormat); }else{ int rid = symbolic_name_to_rid( zSymname ? zSymname : zPageName, "w" ); if(rid<0){ json_set_err(FSL_JSON_E_AMBIGUOUS_UUID, "UUID [%s] is ambiguous.", zSymname); |
︙ | ︙ | |||
227 228 229 230 231 232 233 | /* ** Implementation of /json/wiki/get. ** */ static cson_value * json_wiki_get(){ char const * zPageName; char const * zSymName = NULL; | | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | /* ** Implementation of /json/wiki/get. ** */ static cson_value * json_wiki_get(){ char const * zPageName; char const * zSymName = NULL; int contentFormat = -1; if( !g.perm.RdWiki && !g.perm.Read ){ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' or 'j' access."); return NULL; } zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1); |
︙ | ︙ | |||
306 307 308 309 310 311 312 313 314 315 316 317 318 319 | Blob content = empty_blob; /* wiki page content */ cson_value * nameV; /* wiki page name */ char const * zPageName; /* cstr form of page name */ cson_value * contentV; /* passed-in content */ cson_value * emptyContent = NULL; /* placeholder for empty content. */ cson_value * payV = NULL; /* payload/return value */ cson_string const * jstr = NULL; /* temp for cson_value-to-cson_string conversions. */ unsigned int contentLen = 0; int rid; if( (createMode && !g.perm.NewWiki) || (!createMode && !g.perm.WrWiki)){ json_set_err(FSL_JSON_E_DENIED, "Requires '%c' permissions.", (createMode ? 'f' : 'k')); | > | 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | Blob content = empty_blob; /* wiki page content */ cson_value * nameV; /* wiki page name */ char const * zPageName; /* cstr form of page name */ cson_value * contentV; /* passed-in content */ cson_value * emptyContent = NULL; /* placeholder for empty content. */ cson_value * payV = NULL; /* payload/return value */ cson_string const * jstr = NULL; /* temp for cson_value-to-cson_string conversions. */ char const * zMimeType = 0; unsigned int contentLen = 0; int rid; if( (createMode && !g.perm.NewWiki) || (!createMode && !g.perm.WrWiki)){ json_set_err(FSL_JSON_E_DENIED, "Requires '%c' permissions.", (createMode ? 'f' : 'k')); |
︙ | ︙ | |||
369 370 371 372 373 374 375 | goto error; } jstr = cson_value_get_string(contentV); contentLen = (int)cson_string_length_bytes(jstr); if(contentLen){ blob_append(&content, cson_string_cstr(jstr),contentLen); } | > > > | | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | goto error; } jstr = cson_value_get_string(contentV); contentLen = (int)cson_string_length_bytes(jstr); if(contentLen){ blob_append(&content, cson_string_cstr(jstr),contentLen); } zMimeType = json_find_option_cstr("mimetype","mimetype","M"); wiki_cmd_commit(zPageName, 0==rid, &content, zMimeType, 0); blob_reset(&content); /* Our return value here has a race condition: if this operation is called concurrently for the same wiki page via two requests, payV could reflect the results of the other save operation. */ payV = json_get_wiki_page_by_name( |
︙ | ︙ | |||
438 439 440 441 442 443 444 | } blob_append(&sql,"SELECT" " substr(tagname,6) as name" " FROM tag WHERE tagname GLOB 'wiki-*'", -1); zGlob = json_find_option_cstr("glob",NULL,"g"); if(zGlob && *zGlob){ | | | | | < | | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | } blob_append(&sql,"SELECT" " substr(tagname,6) as name" " FROM tag WHERE tagname GLOB 'wiki-*'", -1); zGlob = json_find_option_cstr("glob",NULL,"g"); if(zGlob && *zGlob){ blob_append_sql(&sql," AND name %s GLOB %Q", fInvert ? "NOT" : "", zGlob); }else{ zGlob = json_find_option_cstr("like",NULL,"l"); if(zGlob && *zGlob){ blob_append_sql(&sql," AND name %s LIKE %Q", fInvert ? "NOT" : "", zGlob); } } blob_append(&sql," ORDER BY lower(name)", -1); db_prepare(&q,"%s", blob_sql_text(&sql)); blob_reset(&sql); listV = cson_value_new_array(); list = cson_value_get_array(listV); while( SQLITE_ROW == db_step(&q) ){ cson_value * v; if( verbose ){ char const * name = db_column_text(&q,0); |
︙ | ︙ | |||
540 541 542 543 544 545 546 | goto manifest; } blob_init(&w1, pW1->zWiki, -1); blob_zero(&w2); blob_init(&w2, pW2->zWiki, -1); blob_zero(&d); | | | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 | goto manifest; } blob_init(&w1, pW1->zWiki, -1); blob_zero(&w2); blob_init(&w2, pW2->zWiki, -1); blob_zero(&d); diffFlags = DIFF_IGNORE_EOLWS | DIFF_STRIP_EOLCR; text_diff(&w2, &w1, &d, 0, diffFlags); blob_reset(&w1); blob_reset(&w2); pay = cson_new_object(); zUuid = json_wiki_get_uuid_for_rid( pW1->rid ); |
︙ | ︙ |
Changes to src/leaf.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to manage the "leaf" table of the ** repository. ** | | | | | > | | | | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to manage the "leaf" table of the ** repository. ** ** The LEAF table contains the rids for all leaves in the check-in DAG. ** A leaf is a check-in that has no children in the same branch. */ #include "config.h" #include "leaf.h" #include <assert.h> /* ** Return true if the check-in with RID=rid is a leaf. ** ** A leaf has no children in the same branch. */ int is_a_leaf(int rid){ int rc; static const char zSql[] = @ SELECT 1 FROM plink @ WHERE pid=%d @ AND coalesce((SELECT value FROM tagxref @ WHERE tagid=%d AND rid=plink.pid), 'trunk') @ =coalesce((SELECT value FROM tagxref @ WHERE tagid=%d AND rid=plink.cid), 'trunk') ; rc = db_int(0, zSql /*works-like:"%d,%d,%d"*/, rid, TAG_BRANCH, TAG_BRANCH); return rc==0; } /* ** Count the number of primary non-branch children for the given check-in. ** ** A primary child is one where the parent is the primary parent, not ** a merge parent. A "leaf" is a node that has zero children of any ** kind. This routine counts only primary children. ** ** A non-branch child is one which is on the same branch as the parent. */ int count_nonbranch_children(int pid){ int nNonBranch = 0; static Stmt q; static const char zSql[] = @ SELECT count(*) FROM plink @ WHERE pid=:pid AND isprim @ AND coalesce((SELECT value FROM tagxref @ WHERE tagid=%d AND rid=plink.pid), 'trunk') @ =coalesce((SELECT value FROM tagxref @ WHERE tagid=%d AND rid=plink.cid), 'trunk') ; db_static_prepare(&q, zSql /*works-like: "%d,%d"*/, TAG_BRANCH, TAG_BRANCH); db_bind_int(&q, ":pid", pid); if( db_step(&q)==SQLITE_ROW ){ nNonBranch = db_column_int(&q, 0); } db_reset(&q); return nNonBranch; } /* ** Recompute the entire LEAF table. ** ** This can be expensive (5 seconds or so) for a really large repository. ** So it is only done for things like a rebuild. */ void leaf_rebuild(void){ db_multi_exec( "DELETE FROM leaf;" "INSERT OR IGNORE INTO leaf" " SELECT cid FROM plink" " EXCEPT" " SELECT pid FROM plink" " WHERE coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND rid=plink.pid),'trunk')" " == coalesce((SELECT value FROM tagxref" " WHERE tagid=%d AND rid=plink.cid),'trunk')", TAG_BRANCH, TAG_BRANCH ); } /* ** A bag of check-ins whose leaf status needs to be checked. */ static Bag needToCheck; /* ** Check to see if check-in "rid" is a leaf and either add it to the LEAF ** table if it is, or remove it if it is not. */ void leaf_check(int rid){ static Stmt checkIfLeaf; static Stmt addLeaf; static Stmt removeLeaf; int rc; |
︙ | ︙ | |||
156 157 158 159 160 161 162 | /* ** Schedule a leaf check for "rid" and its parents. */ void leaf_eventually_check(int rid){ static Stmt parentsOf; | | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | /* ** Schedule a leaf check for "rid" and its parents. */ void leaf_eventually_check(int rid){ static Stmt parentsOf; db_static_prepare(&parentsOf, "SELECT pid FROM plink WHERE cid=:rid AND pid>0" ); db_bind_int(&parentsOf, ":rid", rid); bag_insert(&needToCheck, rid); while( db_step(&parentsOf)==SQLITE_ROW ){ bag_insert(&needToCheck, db_column_int(&parentsOf, 0)); } |
︙ | ︙ |
Added src/loadctrl.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to check the host load-average and abort ** CPU-intensive operations if the load-average is too high. */ #include "config.h" #include "loadctrl.h" #include <assert.h> /* ** Return the load average for the host processor */ double load_average(void){ #if !defined(_WIN32) && !defined(FOSSIL_OMIT_LOAD_AVERAGE) double a[3]; if( getloadavg(a, 3)>0 ){ return a[0]>=0.000001 ? a[0] : 0.000001; } #endif return 0.0; } /* ** COMMAND: test-loadavg ** %fossil test-loadavg ** ** Print the load average on the host machine. */ void loadavg_test_cmd(void){ fossil_print("load-average: %f\n", load_average()); } /* ** Abort the current operation of the load average of the host computer ** is too high. */ void load_control(void){ double mxLoad = atof(db_get("max-loadavg", "0")); if( mxLoad<=0.0 || mxLoad>=load_average() ) return; style_header("Server Overload"); @ <h2>The server load is currently too high. @ Please try again later.</h2> @ <p>Current load average: %f(load_average()).<br /> @ Load average limit: %f(mxLoad)</p> style_footer(); cgi_set_status(503,"Server Overload"); cgi_reply(); exit(0); } |
Changes to src/login.c.
︙ | ︙ | |||
39 40 41 42 43 44 45 | ** not really the point. The anonymous login keeps search-engine ** crawlers and site download tools like wget from walking change ** logs and downloading diffs of very version of the archive that ** has ever existed, and things like that. */ #include "config.h" #include "login.h" | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | ** not really the point. The anonymous login keeps search-engine ** crawlers and site download tools like wget from walking change ** logs and downloading diffs of very version of the archive that ** has ever existed, and things like that. */ #include "config.h" #include "login.h" #if defined(_WIN32) # include <windows.h> /* for Sleep */ # if defined(__MINGW32__) || defined(_MSC_VER) # define sleep Sleep /* windows does not have sleep, but Sleep */ # endif #endif #include <time.h> |
︙ | ︙ | |||
110 111 112 113 114 115 116 | }else{ fossil_redirect_home(); } } /* ** The IP address of the client is stored as part of login cookies. | | | | | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | }else{ fossil_redirect_home(); } } /* ** The IP address of the client is stored as part of login cookies. ** But some clients are behind firewalls that shift the IP address ** with each HTTP request. To allow such (broken) clients to log in, ** extract just a prefix of the IP address. */ static char *ipPrefix(const char *zIP){ int i, j; static int ip_prefix_terms = -1; if( ip_prefix_terms<0 ){ ip_prefix_terms = db_get_int("ip-prefix-terms",2); } |
︙ | ︙ | |||
206 207 208 209 210 211 212 | ** Searches for the user ID matching the given name and password. ** On success it returns a positive value. On error it returns 0. ** On serious (DB-level) error it will probably exit. ** ** zPassword may be either the plain-text form or the encrypted ** form of the user's password. */ | | | | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | ** Searches for the user ID matching the given name and password. ** On success it returns a positive value. On error it returns 0. ** On serious (DB-level) error it will probably exit. ** ** zPassword may be either the plain-text form or the encrypted ** form of the user's password. */ int login_search_uid(const char *zUsername, const char *zPasswd){ char *zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0); int const uid = db_int(0, "SELECT uid FROM user" " WHERE login=%Q" " AND length(cap)>0 AND length(pw)>0" " AND login NOT IN ('anonymous','nobody','developer','reader')" " AND (pw=%Q OR (length(pw)<>40 AND pw=%Q))", |
︙ | ︙ | |||
229 230 231 232 233 234 235 | ** Generates a login cookie value for a non-anonymous user. ** ** The zHash parameter must be a random value which must be ** subsequently stored in user.cookie for later validation. ** ** The returned memory should be free()d after use. */ | | | | | | | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | ** Generates a login cookie value for a non-anonymous user. ** ** The zHash parameter must be a random value which must be ** subsequently stored in user.cookie for later validation. ** ** The returned memory should be free()d after use. */ char *login_gen_user_cookie_value(const char *zUsername, const char *zHash){ char *zProjCode = db_get("project-code",NULL); char *zCode = abbreviated_project_code(zProjCode); free(zProjCode); assert((zUsername && *zUsername) && "Invalid user data."); return mprintf("%s/%z/%s", zHash, zCode, zUsername); } /* ** Generates a login cookie for NON-ANONYMOUS users. Note that this ** function "could" figure out the uid by itself but it currently ** doesn't because the code which calls this already has the uid. ** ** This function also updates the user.cookie, user.ipaddr, ** and user.cexpire fields for the given user. ** ** If zDest is not NULL then the generated cookie is copied to ** *zDdest and ownership is transfered to the caller (who should ** eventually pass it to free()). */ void login_set_user_cookie( const char *zUsername, /* User's name */ int uid, /* User's ID */ char **zDest /* Optional: store generated cookie value. */ ){ const char *zCookieName = login_cookie_name(); const char *zExpire = db_get("cookie-expire","8766"); int expires = atoi(zExpire)*3600; char *zHash; char *zCookie; const char *zIpAddr = PD("REMOTE_ADDR","nil"); /* IP address of user */ char *zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */ assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data."); zHash = db_text(0, "SELECT cookie FROM user" " WHERE uid=%d" " AND ipaddr=%Q" |
︙ | ︙ | |||
301 302 303 304 305 306 307 | ** ** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR ** is used. ** ** If zCookieDest is not NULL then the generated cookie is assigned to ** *zCookieDest and the caller must eventually free() it. */ | | | | | | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | ** ** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR ** is used. ** ** If zCookieDest is not NULL then the generated cookie is assigned to ** *zCookieDest and the caller must eventually free() it. */ void login_set_anon_cookie(const char *zIpAddr, char **zCookieDest ){ const char *zNow; /* Current time (julian day number) */ char *zCookie; /* The login cookie */ const char *zCookieName; /* Name of the login cookie */ Blob b; /* Blob used during cookie construction */ char *zRemoteAddr; /* Abbreviated IP address */ if(!zIpAddr){ zIpAddr = PD("REMOTE_ADDR","nil"); } zRemoteAddr = ipPrefix(zIpAddr); zCookieName = login_cookie_name(); zNow = db_text("0", "SELECT julianday('now')"); assert( zCookieName && zRemoteAddr && zIpAddr && zNow ); |
︙ | ︙ | |||
342 343 344 345 346 347 348 | ** ** This is a no-op if g.userUid is 0. */ void login_clear_login_data(){ if(!g.userUid){ return; }else{ | | | | < < < < | 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | ** ** This is a no-op if g.userUid is 0. */ void login_clear_login_data(){ if(!g.userUid){ return; }else{ const char *cookie = login_cookie_name(); /* To logout, change the cookie value to an empty string */ cgi_set_cookie(cookie, "", login_cookie_path(), -86400); db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, " " cexpire=0 WHERE uid=%d" " AND login NOT IN ('anonymous','nobody'," " 'developer','reader')", g.userUid); cgi_replace_parameter(cookie, NULL); cgi_replace_parameter("anon", NULL); } } /* ** Return true if the prefix of zStr matches zPattern. Return false if ** they are different. ** |
︙ | ︙ | |||
393 394 395 396 397 398 399 | if( prefix_match("spider", zAgent+i) ) return 0; if( prefix_match("crawl", zAgent+i) ) return 0; /* If a URI appears in the User-Agent, it is probably a bot */ if( strncmp("http", zAgent+i,4)==0 ) return 0; } if( strncmp(zAgent, "Mozilla/", 8)==0 ){ if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */ | | | | > | | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | if( prefix_match("spider", zAgent+i) ) return 0; if( prefix_match("crawl", zAgent+i) ) return 0; /* If a URI appears in the User-Agent, it is probably a bot */ if( strncmp("http", zAgent+i,4)==0 ) return 0; } if( strncmp(zAgent, "Mozilla/", 8)==0 ){ if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */ if( sqlite3_strglob("*Firefox/[1-9]*", zAgent)==0 ) return 1; if( sqlite3_strglob("*Chrome/[1-9]*", zAgent)==0 ) return 1; if( sqlite3_strglob("*(compatible;?MSIE?[1789]*", zAgent)==0 ) return 1; if( sqlite3_strglob("*Trident/[1-9]*;?rv:[1-9]*", zAgent)==0 ) return 1; /* IE11+ */ if( sqlite3_strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent)==0 ) return 1; return 0; } if( strncmp(zAgent, "Opera/", 6)==0 ) return 1; if( strncmp(zAgent, "Safari/", 7)==0 ) return 1; if( strncmp(zAgent, "Lynx/", 5)==0 ) return 1; if( strncmp(zAgent, "NetSurf/", 8)==0 ) return 1; return 0; |
︙ | ︙ | |||
448 449 450 451 452 453 454 | rc = rc | (buf1[i] ^ buf2[i]); } } sqlite3_result_int(context, rc); } /* | | < | | | > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > > | | > > | > > > | | | | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 | rc = rc | (buf1[i] ^ buf2[i]); } } sqlite3_result_int(context, rc); } /* ** Return true if the current page was reached by a redirect from the /login ** page. */ int referred_from_login(void){ const char *zReferer = P("HTTP_REFERER"); char *zPattern; int rc; if( zReferer==0 ) return 0; zPattern = mprintf("%s/login*", g.zBaseURL); rc = sqlite3_strglob(zPattern, zReferer)==0; fossil_free(zPattern); return rc; } /* ** There used to be a page named "my" that was designed to show information ** about a specific user. The "my" page was linked from the "Logged in as USER" ** line on the title bar. The "my" page was never completed so it is now ** removed. Use this page as a placeholder in older installations. ** ** WEBPAGE: login ** WEBPAGE: logout ** WEBPAGE: my ** ** The login/logout page. Parameters: ** ** g=URL Jump back to this URL after login completes ** anon The g=URL is not accessible by "nobody" but is ** accessible by "anonymous" */ void login_page(void){ const char *zUsername, *zPasswd; const char *zNew1, *zNew2; const char *zAnonPw = 0; const char *zGoto = P("g"); int anonFlag; /* Login as "anonymous" would be useful */ char *zErrMsg = ""; int uid; /* User id logged in user */ char *zSha1Pw; const char *zIpAddr; /* IP address of requestor */ const char *zReferer; login_check_credentials(); if( login_wants_https_redirect() ){ const char *zQS = P("QUERY_STRING"); if( zQS==0 ){ zQS = ""; }else if( zQS[0]!=0 ){ zQS = mprintf("?%s", zQS); } cgi_redirectf("%s%s%s", g.zHttpsURL, P("PATH_INFO"), zQS); return; } sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, constant_time_cmp_function, 0, 0); zUsername = P("u"); zPasswd = P("p"); anonFlag = g.zLogin==0 && PB("anon"); /* Handle log-out requests */ if( P("out") ){ login_clear_login_data(); redirect_to_g(); return; } /* Deal with password-change requests */ if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){ /* The user requests a password change */ zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); if( db_int(1, "SELECT 0 FROM user" " WHERE uid=%d" " AND (constant_time_cmp(pw,%Q)=0" " OR constant_time_cmp(pw,%Q)=0)", g.userUid, zSha1Pw, zPasswd) ){ sleep(1); zErrMsg = @ <p><span class="loginError"> @ You entered an incorrect old password while attempting to change @ your password. Your password is unchanged. @ </span></p> ; }else if( fossil_strcmp(zNew1,zNew2)!=0 ){ zErrMsg = @ <p><span class="loginError"> @ The two copies of your new passwords do not match. @ Your password is unchanged. @ </span></p> ; }else{ char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0); |
︙ | ︙ | |||
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | }else{ redirect_to_g(); return; } } } zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */ uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs")); if( uid>0 ){ login_set_anon_cookie(zIpAddr, NULL); record_login_attempt("anonymous", zIpAddr, 1); redirect_to_g(); } if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){ /* Attempting to log in as a user other than anonymous. */ uid = login_search_uid(zUsername, zPasswd); if( uid<=0 ){ sleep(1); | > | > | | > > > > > > > > > > > > > > > > > > > > > > > | | < | < < < < < < < < | | | < < < < < < < | < | | | > > > > | | | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 | }else{ redirect_to_g(); return; } } } zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */ zReferer = P("HTTP_REFERER"); uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs")); if( uid>0 ){ login_set_anon_cookie(zIpAddr, NULL); record_login_attempt("anonymous", zIpAddr, 1); redirect_to_g(); } if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){ /* Attempting to log in as a user other than anonymous. */ uid = login_search_uid(zUsername, zPasswd); if( uid<=0 ){ sleep(1); zErrMsg = @ <p><span class="loginError"> @ You entered an unknown user or an incorrect password. @ </span></p> ; record_login_attempt(zUsername, zIpAddr, 0); }else{ /* Non-anonymous login is successful. Set a cookie of the form: ** ** HASH/PROJECT/LOGIN ** ** where HASH is a random hex number, PROJECT is either project ** code prefix, and LOGIN is the user name. */ login_set_user_cookie(zUsername, uid, NULL); redirect_to_g(); } } style_header("Login/Logout"); style_adunit_config(ADUNIT_OFF); @ %s(zErrMsg) if( zGoto ){ char *zAbbrev = fossil_strdup(zGoto); int i; for(i=0; zAbbrev[i] && zAbbrev[i]!='?'; i++){} zAbbrev[i] = 0; if( g.zLogin ){ @ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b> @ to access <b>%h(zAbbrev)</b>. }else if( anonFlag ){ @ <p>Login as <b>anonymous</b> or any named user @ to access page <b>%h(zAbbrev)</b>. }else{ @ <p>Login as a named user to access page <b>%h(zAbbrev)</b>. } } form_begin(0, "%R/login"); if( zGoto ){ @ <input type="hidden" name="g" value="%h(zGoto)" /> }else if( zReferer && strncmp(g.zBaseURL, zReferer, strlen(g.zBaseURL))==0 ){ @ <input type="hidden" name="g" value="%h(zReferer)" /> } if( anonFlag ){ @ <input type="hidden" name="anon" value="1" /> } if( g.zLogin ){ @ <p>Currently logged in as <b>%h(g.zLogin)</b>. @ <input type="submit" name="out" value="Logout"></p> @ <hr /> @ <p>Change user: } @ <table class="login_out"> @ <tr> @ <td class="login_out_label">User ID:</td> if( anonFlag ){ @ <td><input type="text" id="u" name="u" value="anonymous" size="30" /></td> }else{ @ <td><input type="text" id="u" name="u" value="" size="30" /></td> } @ </tr> @ <tr> @ <td class="login_out_label">Password:</td> @ <td><input type="password" id="p" name="p" value="" size="30" /></td> @ </tr> if( g.zLogin==0 && (anonFlag || zGoto==0) ){ zAnonPw = db_text(0, "SELECT pw FROM user" " WHERE login='anonymous'" " AND cap!=''"); } @ <tr> @ <td></td> @ <td><input type="submit" name="in" value="Login" @ onClick="chngAction(this.form)" /></td> @ </tr> @ </table> @ <script> @ gebi('u').focus() @ function chngAction(form){ if( g.sslNotAvailable==0 && strncmp(g.zBaseURL,"https:",6)!=0 && db_get_boolean("https-login",0) ){ char *zSSL = mprintf("https:%s", &g.zBaseURL[5]); @ if( form.u.value!="anonymous" ){ @ form.action = "%h(zSSL)/login"; @ } } @ } @ </script> @ <p>Pressing the Login button grants permission to store a cookie.</p> if( db_get_boolean("self-register", 0) ){ @ <p>If you do not have an account, you can @ <a href="%R/register?g=%T(P("G"))">create one</a>. } if( zAnonPw ){ unsigned int uSeed = captcha_seed(); const char *zDecoded = captcha_decode(uSeed); int bAutoCaptcha = db_get_boolean("auto-captcha", 0); char *zCaptcha = captcha_render(zDecoded); @ <p><input type="hidden" name="cs" value="%u(uSeed)" /> @ Visitors may enter <b>anonymous</b> as the user-ID with @ the 8-character hexadecimal password shown below:</p> @ <div class="captcha"><table class="captcha"><tr><td><pre> @ %h(zCaptcha) @ </pre></td></tr></table> if( bAutoCaptcha ) { @ <input type="button" value="Fill out captcha" @ onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" /> } @ </div> free(zCaptcha); } @ </form> if( g.perm.Password ){ @ <hr /> @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> form_begin(0, "%R/login"); @ <table> @ <tr><td class="login_out_label">Old Password:</td> @ <td><input type="password" name="p" size="30" /></td></tr> @ <tr><td class="login_out_label">New Password:</td> @ <td><input type="password" name="n1" size="30" /></td></tr> @ <tr><td class="login_out_label">Repeat New Password:</td> @ <td><input type="password" name="n2" size="30" /></td></tr> @ <tr><td></td> @ <td><input type="submit" value="Change Password" /></td></tr> @ </table> @ </form> } style_footer(); } /* ** Attempt to find login credentials for user zLogin on a peer repository ** with project code zCode. Transfer those credentials to the local ** repository. ** ** Return true if a transfer was made and false if not. */ static int login_transfer_credentials( const char *zLogin, /* Login we are looking for */ const char *zCode, /* Project code of peer repository */ const char *zHash, /* HASH from login cookie HASH/CODE/LOGIN */ const char *zRemoteAddr /* Request comes from here */ ){ sqlite3 *pOther = 0; /* The other repository */ sqlite3_stmt *pStmt; /* Query against the other repository */ char *zSQL; /* SQL of the query against other repo */ char *zOtherRepo; /* Filename of the other repository */ int rc; /* Result code from SQLite library functions */ int nXfer = 0; /* Number of credentials transferred */ zOtherRepo = db_text(0, "SELECT value FROM config WHERE name='peer-repo-%q'", zCode ); if( zOtherRepo==0 ) return 0; /* No such peer repository */ rc = sqlite3_open_v2( zOtherRepo, &pOther, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, g.zVfsName ); if( rc==SQLITE_OK ){ sqlite3_create_function(pOther,"now",0,SQLITE_UTF8,0,db_now_function,0,0); sqlite3_create_function(pOther, "constant_time_cmp", 2, SQLITE_UTF8, 0, constant_time_cmp_function, 0, 0); sqlite3_busy_timeout(pOther, 5000); zSQL = mprintf( "SELECT cexpire FROM user" " WHERE login=%Q" " AND ipaddr=%Q" " AND length(cap)>0" " AND length(pw)>0" |
︙ | ︙ | |||
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 | } sqlite3_finalize(pStmt); } sqlite3_close(pOther); fossil_free(zOtherRepo); return nXfer; } /* ** Lookup the uid for a non-built-in user with zLogin and zCookie and ** zRemoteAddr. Return 0 if not found. ** ** Note that this only searches for logged-in entries with matching ** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr) ** entries. */ static int login_find_user( const char *zLogin, /* User name */ const char *zCookie, /* Login cookie value */ const char *zRemoteAddr /* Abbreviated IP address for valid login */ ){ int uid; | > > > > > > > > > > > | < < < | > > > > > > > > > > > > > > > > > | | | > > > > > | > | 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 | } sqlite3_finalize(pStmt); } sqlite3_close(pOther); fossil_free(zOtherRepo); return nXfer; } /* ** Return TRUE if zLogin is one of the special usernames */ int login_is_special(const char *zLogin){ if( fossil_strcmp(zLogin, "anonymous")==0 ) return 1; if( fossil_strcmp(zLogin, "nobody")==0 ) return 1; if( fossil_strcmp(zLogin, "developer")==0 ) return 1; if( fossil_strcmp(zLogin, "reader")==0 ) return 1; return 0; } /* ** Lookup the uid for a non-built-in user with zLogin and zCookie and ** zRemoteAddr. Return 0 if not found. ** ** Note that this only searches for logged-in entries with matching ** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr) ** entries. */ static int login_find_user( const char *zLogin, /* User name */ const char *zCookie, /* Login cookie value */ const char *zRemoteAddr /* Abbreviated IP address for valid login */ ){ int uid; if( login_is_special(zLogin) ) return 0; uid = db_int(0, "SELECT uid FROM user" " WHERE login=%Q" " AND ipaddr=%Q" " AND cexpire>julianday('now')" " AND length(cap)>0" " AND length(pw)>0" " AND constant_time_cmp(cookie,%Q)=0", zLogin, zRemoteAddr, zCookie ); return uid; } /* ** Return true if it is appropriate to redirect login requests to HTTPS. ** ** Redirect to https is appropriate if all of the above are true: ** (1) The redirect-to-https flag is set ** (2) The current connection is http, not https or ssh ** (3) The sslNotAvailable flag is clear */ int login_wants_https_redirect(void){ if( g.sslNotAvailable ) return 0; if( db_get_boolean("redirect-to-https",0)==0 ) return 0; if( P("HTTPS")!=0 ) return 0; return 1; } /* ** This routine examines the login cookie to see if it exists and ** is valid. If the login cookie checks out, it then sets global ** variables appropriately. ** ** g.userUid Database USER.UID value. Might be -1 for "nobody" ** g.zLogin Database USER.LOGIN value. NULL for user "nobody" ** g.perm Permissions granted to this user ** g.anon Permissions that would be available to anonymous ** g.isHuman True if the user is human, not a spider or robot ** */ void login_check_credentials(void){ int uid = 0; /* User id */ const char *zCookie; /* Text of the login cookie */ const char *zIpAddr; /* Raw IP address of the requestor */ char *zRemoteAddr; /* Abbreviated IP address of the requestor */ const char *zCap = 0; /* Capability string */ const char *zPublicPages = 0; /* GLOB patterns of public pages */ const char *zLogin = 0; /* Login user for credentials */ /* Only run this check once. */ if( g.userUid!=0 ) return; sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, constant_time_cmp_function, 0, 0); /* If the HTTP connection is coming over 127.0.0.1 and if ** local login is disabled and if we are using HTTP and not HTTPS, ** then there is no need to check user credentials. ** ** This feature allows the "fossil ui" command to give the user ** full access rights without having to log in. */ zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil")); if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 || (g.fSshClient & CGI_SSH_CLIENT)!=0 ) && g.useLocalauth && db_get_int("localauth",0)==0 && P("HTTPS")==0 ){ if( g.localOpen ) zLogin = db_lget("default-user",0); if( zLogin!=0 ){ uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin); }else{ uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); } g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); zCap = "sx"; g.noPswd = 1; g.isHuman = 1; sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost"); } |
︙ | ︙ | |||
833 834 835 836 837 838 839 | /* Cookies of the form "HASH/TIME/anonymous". The TIME must not be ** too old and the sha1 hash of TIME/IPADDR/SECRET must match HASH. ** SECRET is the "captcha-secret" value in the repository. */ double rTime = atof(zArg); Blob b; blob_zero(&b); | | | | 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 | /* Cookies of the form "HASH/TIME/anonymous". The TIME must not be ** too old and the sha1 hash of TIME/IPADDR/SECRET must match HASH. ** SECRET is the "captcha-secret" value in the repository. */ double rTime = atof(zArg); Blob b; blob_zero(&b); blob_appendf(&b, "%s/%s/%s", zArg, zRemoteAddr, db_get("captcha-secret","")); sha1sum_blob(&b, &b); if( fossil_strcmp(zHash, blob_str(&b))==0 ){ uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" " AND length(cap)>0" " AND length(pw)>0" " AND %.17g+0.25>julianday('now')", rTime ); } |
︙ | ︙ | |||
950 951 952 953 954 955 956 | /* ** Memory of settings */ static int login_anon_once = 1; /* | | | > > > > | | > | > > > | > | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 | /* ** Memory of settings */ static int login_anon_once = 1; /* ** Add to g.perm the default privileges of users "nobody" and/or "anonymous" ** as appropriate for the user g.zLogin. ** ** This routine also sets up g.anon to be either a copy of g.perm for ** all logged in uses, or the privileges that would be available to "anonymous" ** if g.zLogin==0 (meaning that the user is "nobody"). */ void login_set_anon_nobody_capabilities(void){ if( login_anon_once ){ const char *zCap; /* All users get privileges from "nobody" */ zCap = db_text("", "SELECT cap FROM user WHERE login = 'nobody'"); login_set_capabilities(zCap, 0); zCap = db_text("", "SELECT cap FROM user WHERE login = 'anonymous'"); if( g.zLogin && fossil_strcmp(g.zLogin, "nobody")!=0 ){ /* All logged-in users inherit privileges from "anonymous" */ login_set_capabilities(zCap, 0); g.anon = g.perm; }else{ /* Record the privileges of anonymous in g.anon */ g.anon = g.perm; login_set_capabilities(zCap, LOGIN_ANON); } login_anon_once = 0; } } /* ** Flags passed into the 2nd argument of login_set/replace_capabilities(). */ #if INTERFACE #define LOGIN_IGNORE_UV 0x01 /* Ignore "u" and "v" */ #define LOGIN_ANON 0x02 /* Use g.anon instead of g.perm */ #endif /* ** Adds all capability flags in zCap to g.perm or g.anon. */ void login_set_capabilities(const char *zCap, unsigned flags){ int i; FossilUserPerms *p = (flags & LOGIN_ANON) ? &g.anon : &g.perm; if(NULL==zCap){ return; } for(i=0; zCap[i]; i++){ switch( zCap[i] ){ case 's': p->Setup = 1; /* Fall thru into Admin */ case 'a': p->Admin = p->RdTkt = p->WrTkt = p->Zip = p->RdWiki = p->WrWiki = p->NewWiki = p->ApndWiki = p->Hyperlink = p->Clone = p->NewTkt = p->Password = p->RdAddr = p->TktFmt = p->Attach = p->ApndTkt = p->ModWiki = p->ModTkt = 1; /* Fall thru into Read/Write */ case 'i': p->Read = p->Write = 1; break; case 'o': p->Read = 1; break; case 'z': p->Zip = 1; break; case 'd': p->Delete = 1; break; case 'h': p->Hyperlink = 1; break; case 'g': p->Clone = 1; break; case 'p': p->Password = 1; break; case 'j': p->RdWiki = 1; break; case 'k': p->WrWiki = p->RdWiki = p->ApndWiki =1; break; case 'm': p->ApndWiki = 1; break; case 'f': p->NewWiki = 1; break; case 'l': p->ModWiki = 1; break; case 'e': p->RdAddr = 1; break; case 'r': p->RdTkt = 1; break; case 'n': p->NewTkt = 1; break; case 'w': p->WrTkt = p->RdTkt = p->NewTkt = p->ApndTkt = 1; break; case 'c': p->ApndTkt = 1; break; case 'q': p->ModTkt = 1; break; case 't': p->TktFmt = 1; break; case 'b': p->Attach = 1; break; case 'x': p->Private = 1; break; /* The "u" privileges is a little different. It recursively ** inherits all privileges of the user named "reader" */ case 'u': { if( (flags & LOGIN_IGNORE_UV)==0 ){ const char *zUser; zUser = db_text("", "SELECT cap FROM user WHERE login='reader'"); login_set_capabilities(zUser, flags | LOGIN_IGNORE_UV); } break; } /* The "v" privileges is a little different. It recursively ** inherits all privileges of the user named "developer" */ case 'v': { if( (flags & LOGIN_IGNORE_UV)==0 ){ const char *zDev; zDev = db_text("", "SELECT cap FROM user WHERE login='developer'"); login_set_capabilities(zDev, flags | LOGIN_IGNORE_UV); } |
︙ | ︙ | |||
1058 1059 1060 1061 1062 1063 1064 | } /* ** If the current login lacks any of the capabilities listed in ** the input, then return 0. If all capabilities are present, then ** return 1. */ | | > | | | | | | | | | | | | | | | | | | | | | | | | 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 | } /* ** If the current login lacks any of the capabilities listed in ** the input, then return 0. If all capabilities are present, then ** return 1. */ int login_has_capability(const char *zCap, int nCap, u32 flgs){ int i; int rc = 1; FossilUserPerms *p = (flgs & LOGIN_ANON) ? &g.anon : &g.perm; if( nCap<0 ) nCap = strlen(zCap); for(i=0; i<nCap && rc && zCap[i]; i++){ switch( zCap[i] ){ case 'a': rc = p->Admin; break; case 'b': rc = p->Attach; break; case 'c': rc = p->ApndTkt; break; case 'd': rc = p->Delete; break; case 'e': rc = p->RdAddr; break; case 'f': rc = p->NewWiki; break; case 'g': rc = p->Clone; break; case 'h': rc = p->Hyperlink; break; case 'i': rc = p->Write; break; case 'j': rc = p->RdWiki; break; case 'k': rc = p->WrWiki; break; case 'l': rc = p->ModWiki; break; case 'm': rc = p->ApndWiki; break; case 'n': rc = p->NewTkt; break; case 'o': rc = p->Read; break; case 'p': rc = p->Password; break; case 'q': rc = p->ModTkt; break; case 'r': rc = p->RdTkt; break; case 's': rc = p->Setup; break; case 't': rc = p->TktFmt; break; /* case 'u': READER */ /* case 'v': DEVELOPER */ case 'w': rc = p->WrTkt; break; case 'x': rc = p->Private; break; /* case 'y': */ case 'z': rc = p->Zip; break; default: rc = 0; break; } } return rc; } /* |
︙ | ︙ | |||
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 | g.zLogin = fossil_strdup(zUser); /* Set the capabilities */ login_set_capabilities(zCap, 0); login_anon_once = 1; login_set_anon_nobody_capabilities(); } /* ** Call this routine when the credential check fails. It causes ** a redirect to the "login" page. */ | > > > > > > > > > > > > > > | > > > > > > | > > > > > > | < < < | | 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 | g.zLogin = fossil_strdup(zUser); /* Set the capabilities */ login_set_capabilities(zCap, 0); login_anon_once = 1; login_set_anon_nobody_capabilities(); } /* ** Return true if the user is "nobody" */ int login_is_nobody(void){ return g.zLogin==0 || g.zLogin[0]==0 || fossil_strcmp(g.zLogin,"nobody")==0; } /* ** Return the login name. If no login name is specified, return "nobody". */ const char *login_name(void){ return (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody"; } /* ** Call this routine when the credential check fails. It causes ** a redirect to the "login" page. */ void login_needed(int anonOk){ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_err( FSL_JSON_E_DENIED, NULL, 1 ); fossil_exit(0); /* NOTREACHED */ assert(0); }else #endif /* FOSSIL_ENABLE_JSON */ { const char *zUrl = PD("REQUEST_URI", "index"); const char *zQS = P("QUERY_STRING"); Blob redir; blob_init(&redir, 0, 0); if( login_wants_https_redirect() ){ blob_appendf(&redir, "%s/login?g=%T", g.zHttpsURL, zUrl); }else{ blob_appendf(&redir, "%R/login?g=%T", zUrl); } if( anonOk ) blob_append(&redir, "&anon", 5); if( zQS && zQS[0] ){ blob_appendf(&redir, "&%s", zQS); } cgi_redirect(blob_str(&redir)); /* NOTREACHED */ assert(0); } } /* ** Call this routine if the user lacks g.perm.Hyperlink permission. If ** the anonymous user has Hyperlink permission, then paint a mesage ** to inform the user that much more information is available by ** logging in as anonymous. */ void login_anonymous_available(void){ if( !g.perm.Hyperlink && g.anon.Hyperlink ){ const char *zUrl = PD("REQUEST_URI", "index"); @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br /> @ Use <a href="%R/login?anon=1&g=%T(zUrl)">anonymous login</a> @ to enable hyperlinks.</p> } } /* ** While rendering a form, call this routine to add the Anti-CSRF token ** as a hidden element of the form. |
︙ | ︙ | |||
1196 1197 1198 1199 1200 1201 1202 | ** ** Generate the register page. ** */ void register_page(void){ const char *zUsername, *zPasswd, *zConfirm, *zContact, *zCS, *zPw, *zCap; unsigned int uSeed; | | | 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 | ** ** Generate the register page. ** */ void register_page(void){ const char *zUsername, *zPasswd, *zConfirm, *zContact, *zCS, *zPw, *zCap; unsigned int uSeed; const char *zDecoded; char *zCaptcha; if( !db_get_boolean("self-register", 0) ){ style_header("Registration not possible"); @ <p>This project does not allow user self-registration. Please contact the @ project administrator to obtain an account.</p> style_footer(); return; |
︙ | ︙ | |||
1253 1254 1255 1256 1257 1258 1259 | @ <p><span class="loginError"> @ %s(zUsername) already exists. @ </span></p> }else{ char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0); int uid; db_multi_exec( | | | | 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 | @ <p><span class="loginError"> @ %s(zUsername) already exists. @ </span></p> }else{ char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0); int uid; db_multi_exec( "INSERT INTO user(login,pw,cap,info,mtime)" "VALUES(%B,%Q,%B,%B,strftime('%%s','now'))", &login, zPw, &caps, &contact ); free(zPw); /* The user is registered, now just log him in. */ uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUsername); login_set_user_cookie( zUsername, uid, NULL ); |
︙ | ︙ | |||
1345 1346 1347 1348 1349 1350 1351 | Stmt q; /* Query of all peer-* entries in CONFIG */ if( zPrefix==0 ) zPrefix = ""; if( zSuffix==0 ) zSuffix = ""; if( pzErrorMsg ) *pzErrorMsg = 0; zSelfCode = abbreviated_project_code(db_get("project-code", "x")); blob_zero(&err); | | | > > > > | | 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 | Stmt q; /* Query of all peer-* entries in CONFIG */ if( zPrefix==0 ) zPrefix = ""; if( zSuffix==0 ) zSuffix = ""; if( pzErrorMsg ) *pzErrorMsg = 0; zSelfCode = abbreviated_project_code(db_get("project-code", "x")); blob_zero(&err); db_prepare(&q, "SELECT name, value FROM config" " WHERE name GLOB 'peer-repo-*'" " AND name <> 'peer-repo-%q'" " ORDER BY +value", zSelfCode ); while( db_step(&q)==SQLITE_ROW ){ const char *zRepoName = db_column_text(&q, 1); if( file_size(zRepoName)<0 ){ /* Silently remove non-existent repositories from the login group. */ const char *zLabel = db_column_text(&q, 0); db_multi_exec( "DELETE FROM config WHERE name GLOB 'peer-*-%q'", &zLabel[10] ); continue; } rc = sqlite3_open_v2( zRepoName, &pPeer, SQLITE_OPEN_READWRITE, g.zVfsName ); if( rc!=SQLITE_OK ){ blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName, sqlite3_errmsg(pPeer), zSuffix); nErr++; sqlite3_close(pPeer); continue; } sqlite3_create_function(pPeer, "shared_secret", 3, SQLITE_UTF8, 0, sha1_shared_secret_sql_function, 0, 0); sqlite3_create_function(pPeer, "now", 0,SQLITE_UTF8,0,db_now_function,0,0); sqlite3_busy_timeout(pPeer, 5000); zErr = 0; rc = sqlite3_exec(pPeer, zSql, 0, 0, &zErr); if( zErr ){ blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName, zErr, zSuffix); sqlite3_free(zErr); nErr++; |
︙ | ︙ | |||
1423 1424 1425 1426 1427 1428 1429 | char *zSelfProjCode; /* Our project-code */ char *zSql; /* SQL to run on all peers */ const char *zSelf; /* The ATTACH name of our repository */ *pzErrMsg = 0; /* Default to no errors */ zSelf = db_name("repository"); | | | | | > > > > | | 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 | char *zSelfProjCode; /* Our project-code */ char *zSql; /* SQL to run on all peers */ const char *zSelf; /* The ATTACH name of our repository */ *pzErrMsg = 0; /* Default to no errors */ zSelf = db_name("repository"); /* Get the full pathname of the other repository */ file_canonical_name(zRepo, &fullName, 0); zRepo = fossil_strdup(blob_str(&fullName)); blob_reset(&fullName); /* Get the full pathname for our repository. Also the project code ** and project name for ourself. */ file_canonical_name(g.zRepositoryName, &fullName, 0); zSelfRepo = fossil_strdup(blob_str(&fullName)); blob_reset(&fullName); zSelfProjCode = db_get("project-code", "unknown"); zSelfLabel = db_get("project-name", 0); if( zSelfLabel==0 ){ zSelfLabel = zSelfProjCode; } /* Make sure we are not trying to join ourselves */ if( fossil_strcmp(zRepo, zSelfRepo)==0 ){ *pzErrMsg = mprintf("The \"other\" repository is the same as this one."); return; } /* Make sure the other repository is a valid Fossil database */ if( file_size(zRepo)<0 ){ *pzErrMsg = mprintf("repository file \"%s\" does not exist", zRepo); return; } rc = sqlite3_open_v2( zRepo, &pOther, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, g.zVfsName ); if( rc!=SQLITE_OK ){ *pzErrMsg = fossil_strdup(sqlite3_errmsg(pOther)); }else{ rc = sqlite3_exec(pOther, "SELECT count(*) FROM user", 0, 0, pzErrMsg); } sqlite3_close(pOther); if( rc ) return; /* Attach the other repository. Make sure the username/password is |
︙ | ︙ | |||
1485 1486 1487 1488 1489 1490 1491 | /* Create all the necessary CONFIG table entries on both the ** other repository and on our own repository. */ zSelfProjCode = abbreviated_project_code(zSelfProjCode); zOtherProjCode = abbreviated_project_code(zOtherProjCode); db_begin_transaction(); db_multi_exec( | | | | | | 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 | /* Create all the necessary CONFIG table entries on both the ** other repository and on our own repository. */ zSelfProjCode = abbreviated_project_code(zSelfProjCode); zOtherProjCode = abbreviated_project_code(zOtherProjCode); db_begin_transaction(); db_multi_exec( "DELETE FROM \"%w\".config WHERE name GLOB 'peer-*';" "INSERT INTO \"%w\".config(name,value) VALUES('peer-repo-%q',%Q);" "INSERT INTO \"%w\".config(name,value) " " SELECT 'peer-name-%q', value FROM other.config" " WHERE name='project-name';", zSelf, zSelf, zOtherProjCode, zRepo, zSelf, zOtherProjCode ); db_multi_exec( "INSERT OR IGNORE INTO other.config(name,value)" " VALUES('login-group-name',%Q);" "INSERT OR IGNORE INTO other.config(name,value)" " VALUES('login-group-code',lower(hex(randomblob(8))));", zNewName ); db_multi_exec( "REPLACE INTO \"%w\".config(name,value)" " SELECT name, value FROM other.config" " WHERE name GLOB 'peer-*' OR name GLOB 'login-group-*'", zSelf ); db_end_transaction(0); db_multi_exec("DETACH other"); |
︙ | ︙ |
Changes to src/lookslike.c.
︙ | ︙ | |||
130 131 132 133 134 135 136 137 138 139 140 141 142 143 | } if( j>LENGTH_MASK ){ flags |= LOOK_LONG; /* Very long line -> binary */ } return flags; } /* ** Define the type needed to represent a Unicode (UTF-16) character. */ #ifndef WCHAR_T # ifdef _WIN32 # define WCHAR_T wchar_t | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | } if( j>LENGTH_MASK ){ flags |= LOOK_LONG; /* Very long line -> binary */ } return flags; } /* ** Checks for proper UTF-8. It uses the method described in: ** http://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences ** except for the "overlong form" of \u0000 which is not considered invalid ** here: Some languages like Java and Tcl use it. For UTF-8 characters ** > 7f, the variable 'c2' not necessary means the previous character. ** It's number of higher 1-bits indicate the number of continuation bytes ** that are expected to be followed. E.g. when 'c2' has a value in the range ** 0xc0..0xdf it means that 'c' is expected to contain the last continuation ** byte of a UTF-8 character. A value 0xe0..0xef means that after 'c' one ** more continuation byte is expected. */ int invalid_utf8(const Blob *pContent){ const unsigned char *z = (unsigned char *) blob_buffer(pContent); unsigned int n = blob_size(pContent); unsigned char c, c2; if( n==0 ) return 0; /* Empty file -> OK */ c = *z; while( --n>0 ){ c2 = c; c = *++z; if( c2>=0x80 ){ if( ((c2<0xc2) || (c2>=0xf4) || ((c&0xc0)!=0x80)) && (((c2!=0xf4) || (c>=0x90)) && ((c2!=0xc0) || (c!=0x80))) ){ return LOOK_INVALID; /* Invalid UTF-8 */ } c = (c2 >= 0xe0) ? (c2<<1)+1 : ' '; } } return (c>=0x80) ? LOOK_INVALID : 0; /* Last byte must be ASCII. */ } /* ** Define the type needed to represent a Unicode (UTF-16) character. */ #ifndef WCHAR_T # ifdef _WIN32 # define WCHAR_T wchar_t |
︙ | ︙ | |||
154 155 156 157 158 159 160 | #define UTF16_LENGTH_MASK_SZ (LENGTH_MASK_SZ-(sizeof(WCHAR_T)-sizeof(char))) #define UTF16_LENGTH_MASK ((1<<UTF16_LENGTH_MASK_SZ)-1) /* ** This macro is used to swap the byte order of a UTF-16 character in the ** looks_like_utf16() function. */ | | | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | #define UTF16_LENGTH_MASK_SZ (LENGTH_MASK_SZ-(sizeof(WCHAR_T)-sizeof(char))) #define UTF16_LENGTH_MASK ((1<<UTF16_LENGTH_MASK_SZ)-1) /* ** This macro is used to swap the byte order of a UTF-16 character in the ** looks_like_utf16() function. */ #define UTF16_SWAP(ch) ((((ch) << 8) & 0xff00) | (((ch) >> 8) & 0xff)) #define UTF16_SWAP_IF(expr,ch) ((expr) ? UTF16_SWAP((ch)) : (ch)) /* ** This function attempts to scan each logical line within the blob to ** determine the type of content it appears to contain. The return value ** is a combination of one or more of the LOOK_XXX flags (see above): ** |
︙ | ︙ | |||
194 195 196 197 198 199 200 | ************************************ WARNING ********************************** */ int looks_like_utf16(const Blob *pContent, int bReverse, int stopFlags){ const WCHAR_T *z = (WCHAR_T *)blob_buffer(pContent); unsigned int n = blob_size(pContent); int j, c, flags = LOOK_NONE; /* Assume UTF-16 text, prove otherwise */ | < < > | < < < | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | ************************************ WARNING ********************************** */ int looks_like_utf16(const Blob *pContent, int bReverse, int stopFlags){ const WCHAR_T *z = (WCHAR_T *)blob_buffer(pContent); unsigned int n = blob_size(pContent); int j, c, flags = LOOK_NONE; /* Assume UTF-16 text, prove otherwise */ if( n%sizeof(WCHAR_T) ){ flags |= LOOK_ODD; /* Odd number of bytes -> binary (UTF-8?) */ } if( n<sizeof(WCHAR_T) ) return flags; /* Zero or One byte -> binary (UTF-8?) */ c = *z; if( bReverse ){ c = UTF16_SWAP(c); } if( c==0 ){ flags |= LOOK_NUL; /* NUL character in a file -> binary */ }else if( c=='\r' ){ flags |= LOOK_CR; if( n<(2*sizeof(WCHAR_T)) || UTF16_SWAP_IF(bReverse, z[1])!='\n' ){ flags |= LOOK_LONE_CR; /* More chars, next char is not LF */ } } j = (c!='\n'); if( !j ) flags |= (LOOK_LF | LOOK_LONE_LF); /* Found LF as first char */ while( !(flags&stopFlags) && ((n-=sizeof(WCHAR_T))>=sizeof(WCHAR_T)) ){ int c2 = c; c = *++z; if( bReverse ){ c = UTF16_SWAP(c); } ++j; if( c==0 ){ flags |= LOOK_NUL; /* NUL character in a file -> binary */ |
︙ | ︙ | |||
258 259 260 261 262 263 264 | /* ** This function returns an array of bytes representing the byte-order-mark ** for UTF-8. */ const unsigned char *get_utf8_bom(int *pnByte){ static const unsigned char bom[] = { | | | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | /* ** This function returns an array of bytes representing the byte-order-mark ** for UTF-8. */ const unsigned char *get_utf8_bom(int *pnByte){ static const unsigned char bom[] = { 0xef, 0xbb, 0xbf, 0x00, 0x00, 0x00 }; if( pnByte ) *pnByte = 3; return bom; } /* ** This function returns non-zero if the blob starts with a UTF-8 |
︙ | ︙ | |||
329 330 331 332 333 334 335 336 337 338 339 340 341 342 | /* ** COMMAND: test-looks-like-utf ** ** Usage: %fossil test-looks-like-utf FILENAME ** ** Options: ** --utf8 Ignoring BOM and file size, force UTF-8 checking ** --utf16 Ignoring BOM and file size, force UTF-16 checking ** ** FILENAME is the name of a file to check for textual content in the UTF-8 ** and/or UTF-16 encodings. */ void looks_like_utf_test_cmd(void){ | > > | | | | | < > > > > > > > | | | | | | | > | > | > > | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | /* ** COMMAND: test-looks-like-utf ** ** Usage: %fossil test-looks-like-utf FILENAME ** ** Options: ** -n|--limit <num> Repeat looks-like function <num> times, for ** performance measurement. Default = 1; ** --utf8 Ignoring BOM and file size, force UTF-8 checking ** --utf16 Ignoring BOM and file size, force UTF-16 checking ** ** FILENAME is the name of a file to check for textual content in the UTF-8 ** and/or UTF-16 encodings. */ void looks_like_utf_test_cmd(void){ Blob blob; /* the contents of the specified file */ int fUtf8 = 0; /* return value of starts_with_utf8_bom() */ int fUtf16 = 0; /* return value of starts_with_utf16_bom() */ int fUnicode = 0; /* return value of could_be_utf16() */ int lookFlags = 0; /* output flags from looks_like_utf8/utf16() */ int bRevUtf16 = 0; /* non-zero -> UTF-16 byte order reversed */ int fForceUtf8 = find_option("utf8",0,0)!=0; int fForceUtf16 = find_option("utf16",0,0)!=0; const char *zCount = find_option("limit","n",1); int nRepeat = 1; if( g.argc!=3 ) usage("FILENAME"); if( zCount ){ nRepeat = atoi(zCount); } blob_read_from_file(&blob, g.argv[2]); while( --nRepeat >= 0 ){ fUtf8 = starts_with_utf8_bom(&blob, 0); fUtf16 = starts_with_utf16_bom(&blob, 0, &bRevUtf16); if( fForceUtf8 ){ fUnicode = 0; }else{ fUnicode = could_be_utf16(&blob, 0) || fForceUtf16; } if( fUnicode ){ lookFlags = looks_like_utf16(&blob, bRevUtf16, 0); }else{ lookFlags = looks_like_utf8(&blob, 0)|invalid_utf8(&blob); } } fossil_print("File \"%s\" has %d bytes.\n",g.argv[2],blob_size(&blob)); fossil_print("Starts with UTF-8 BOM: %s\n",fUtf8?"yes":"no"); fossil_print("Starts with UTF-16 BOM: %s\n", fUtf16?(bRevUtf16?"reversed":"yes"):"no"); fossil_print("Looks like UTF-%s: %s\n",fUnicode?"16":"8", (lookFlags&LOOK_BINARY)?"no":"yes"); fossil_print("Has flag LOOK_NUL: %s\n",(lookFlags&LOOK_NUL)?"yes":"no"); |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This module codes the main() procedure that runs first when the ** program is invoked. */ #include "config.h" #include "main.h" #include <string.h> #include <time.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ #if defined(_WIN32) # include <windows.h> #else # include <errno.h> /* errno global */ #endif | > < | > > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This module codes the main() procedure that runs first when the ** program is invoked. */ #include "VERSION.h" #include "config.h" #include "main.h" #include <string.h> #include <time.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ #if defined(_WIN32) # include <windows.h> #else # include <errno.h> /* errno global */ #endif #ifdef FOSSIL_ENABLE_SSL # include "openssl/crypto.h" #endif #if defined(FOSSIL_ENABLE_MINIZ) # define MINIZ_HEADER_FILE_ONLY # include "miniz.c" #else # include <zlib.h> #endif #if INTERFACE #ifdef FOSSIL_ENABLE_TCL # include "tcl.h" #endif #ifdef FOSSIL_ENABLE_JSON # include "cson_amalgamation.h" /* JSON API. */ |
︙ | ︙ | |||
64 65 66 67 68 69 70 | */ struct FossilUserPerms { char Setup; /* s: use Setup screens on web interface */ char Admin; /* a: administrative permission */ char Delete; /* d: delete wiki or tickets */ char Password; /* p: change password */ char Query; /* q: create new reports */ | | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | */ struct FossilUserPerms { char Setup; /* s: use Setup screens on web interface */ char Admin; /* a: administrative permission */ char Delete; /* d: delete wiki or tickets */ char Password; /* p: change password */ char Query; /* q: create new reports */ char Write; /* i: xfer inbound. check-in */ char Read; /* o: xfer outbound. check-out */ char Hyperlink; /* h: enable the display of hyperlinks */ char Clone; /* g: clone */ char RdWiki; /* j: view wiki via web */ char NewWiki; /* f: create new wiki via web */ char ApndWiki; /* m: append to wiki via web */ char WrWiki; /* k: edit wiki via web */ char ModWiki; /* l: approve and publish wiki content (Moderator) */ |
︙ | ︙ | |||
109 110 111 112 113 114 115 | void *xPreEval; /* Optional, called before Tcl_Eval*(). */ void *pPreContext; /* Optional, provided to xPreEval(). */ void *xPostEval; /* Optional, called after Tcl_Eval*(). */ void *pPostContext; /* Optional, provided to xPostEval(). */ }; #endif | < < < | > > > > > > > > > > > | | < < < < < < < < < < < < < < < < < | > | > > > > > > > > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | void *xPreEval; /* Optional, called before Tcl_Eval*(). */ void *pPreContext; /* Optional, provided to xPreEval(). */ void *xPostEval; /* Optional, called after Tcl_Eval*(). */ void *pPostContext; /* Optional, provided to xPostEval(). */ }; #endif struct Global { int argc; char **argv; /* Command-line arguments to the program */ char *nameOfExe; /* Full path of executable. */ const char *zErrlog; /* Log errors to this file, if not NULL */ int isConst; /* True if the output is unchanging & cacheable */ const char *zVfsName; /* The VFS to use for database connections */ sqlite3 *db; /* The connection to the databases */ sqlite3 *dbConfig; /* Separate connection for global_config table */ char *zAuxSchema; /* Main repository aux-schema */ int useAttach; /* True if global_config is attached to repository */ const char *zConfigDbName;/* Path of the config database. NULL if not open */ sqlite3_int64 now; /* Seconds since 1970 */ int repositoryOpen; /* True if the main repository database is open */ char *zRepositoryOption; /* Most recent cached repository option value */ char *zRepositoryName; /* Name of the repository database */ char *zLocalDbName; /* Name of the local database */ const char *zMainDbType;/* "configdb", "localdb", or "repository" */ const char *zConfigDbType; /* "configdb", "localdb", or "repository" */ int localOpen; /* True if the local database is open */ char *zLocalRoot; /* The directory holding the local database */ int minPrefix; /* Number of digits needed for a distinct UUID */ int fSqlTrace; /* True if --sqltrace flag is present */ int fSqlStats; /* True if --sqltrace or --sqlstats are present */ int fSqlPrint; /* True if -sqlprint flag is present */ int fQuiet; /* True if -quiet flag is present */ int fHttpTrace; /* Trace outbound HTTP requests */ int fAnyTrace; /* Any kind of tracing */ char *zHttpAuth; /* HTTP Authorization user:pass information */ int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ int fSshTrace; /* Trace the SSH setup traffic */ int fSshClient; /* HTTP client flags for SSH client */ char *zSshCmd; /* SSH command string */ int fNoSync; /* Do not do an autosync ever. --nosync */ int fIPv4; /* Use only IPv4, not IPv6. --ipv4 */ char *zPath; /* Name of webpage being served */ char *zExtra; /* Extra path information past the webpage name */ char *zBaseURL; /* Full text of the URL being served */ char *zHttpsURL; /* zBaseURL translated to https: */ char *zTop; /* Parent directory of zPath */ const char *zContentType; /* The content type of the input HTTP request */ int iErrPriority; /* Priority of current error message */ char *zErrMsg; /* Text of an error message */ int sslNotAvailable; /* SSL is not available. Do not redirect to https: */ Blob cgiIn; /* Input to an xfer www method */ int cgiOutput; /* Write error and status messages to CGI */ int xferPanic; /* Write error messages in XFER protocol */ int fullHttpReply; /* True for full HTTP reply. False for CGI reply */ Th_Interp *interp; /* The TH1 interpreter */ char *th1Setup; /* The TH1 post-creation setup script, if any */ int th1Flags; /* The TH1 integration state flags */ FILE *httpIn; /* Accept HTTP input from here */ FILE *httpOut; /* Send HTTP output here */ int xlinkClusterOnly; /* Set when cloning. Only process clusters */ int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ int *aCommitFile; /* Array of files to be committed */ int markPrivate; /* All new artifacts are private if true */ int clockSkewSeen; /* True if clocks on client and server out of sync */ int wikiFlags; /* Wiki conversion flags applied to %W */ char isHTTP; /* True if server/CGI modes, else assume CLI. */ char javascriptHyperlink; /* If true, set href= using script, not HTML */ Blob httpHeader; /* Complete text of the HTTP request header */ UrlData url; /* Information about current URL */ const char *zLogin; /* Login name. NULL or "" if not logged in. */ const char *zSSLIdentity; /* Value of --ssl-identity option, filename of ** SSL client identity */ int useLocalauth; /* No login required if from 127.0.0.1 */ int noPswd; /* Logged in without password (on 127.0.0.1) */ int userUid; /* Integer user id */ int isHuman; /* True if access by a human, not a spider or bot */ int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags */ /* Information used to populate the RCVFROM table */ int rcvid; /* The rcvid. 0 if not yet defined. */ char *zIpAddr; /* The remote IP address */ char *zNonce; /* The nonce used for login */ /* permissions available to current user */ struct FossilUserPerms perm; /* permissions available to current user or to "anonymous". ** This is the logical union of perm permissions above with ** the value that perm would take if g.zLogin were "anonymous". */ struct FossilUserPerms anon; #ifdef FOSSIL_ENABLE_TCL /* all Tcl related context necessary for integration */ struct TclContext tcl; #endif /* For defense against Cross-site Request Forgery attacks */ char zCsrfToken[12]; /* Value of the anti-CSRF token */ int okCsrf; /* Anti-CSRF token is present and valid */ int parseCnt[10]; /* Counts of artifacts parsed */ FILE *fDebug; /* Write debug information here, if the file exists */ #ifdef FOSSIL_ENABLE_TH1_HOOKS int fNoThHook; /* Disable all TH1 command/webpage hooks */ #endif int thTrace; /* True to enable TH1 debugging output */ Blob thLog; /* Text of the TH1 debugging output */ int isHome; /* True if rendering the "home" page */ /* Storage for the aux() and/or option() SQL function arguments */ int nAux; /* Number of distinct aux() or option() values */ |
︙ | ︙ | |||
236 237 238 239 240 241 242 | responses and always exit() with code 0 to avoid an HTTP 500 error. */ int resultCode; /* used for passing back specific codes ** from /json callbacks. */ int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */ cson_output_opt outOpt; /* formatting options for JSON mode. */ | | | | | | | | | | | | | | | | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | responses and always exit() with code 0 to avoid an HTTP 500 error. */ int resultCode; /* used for passing back specific codes ** from /json callbacks. */ int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */ cson_output_opt outOpt; /* formatting options for JSON mode. */ cson_value *authToken; /* authentication token */ const char *jsonp; /* Name of JSONP function wrapper. */ unsigned char dispatchDepth /* Tells JSON command dispatching which argument we are currently working on. For this purpose, arg#0 is the "json" path/CLI arg. */; struct { /* "garbage collector" */ cson_value *v; cson_array *a; } gc; struct { /* JSON POST data. */ cson_value *v; cson_array *a; int offset; /* Tells us which PATH_INFO/CLI args part holds the "json" command, so that we can account for sub-repos and path prefixes. This is handled differently for CLI and CGI modes. */ const char *commandStr /*"command" request param.*/; } cmd; struct { /* JSON POST data. */ cson_value *v; cson_object *o; } post; struct { /* GET/COOKIE params in JSON mode. */ cson_value *v; cson_object *o; } param; struct { cson_value *v; cson_object *o; } reqPayload; /* request payload object (if any) */ cson_array *warnings; /* response warnings */ int timerId; /* fetched from fossil_timer_start() */ } json; #endif /* FOSSIL_ENABLE_JSON */ }; /* ** Macro for debugging: |
︙ | ︙ | |||
306 307 308 309 310 311 312 313 314 315 316 | ** defined in the page_index.h header file which is automatically ** generated by mkindex.c program. */ static int name_search( const char *zName, /* The name we are looking for */ const NameMap *aMap, /* Search in this array */ int nMap, /* Number of slots in aMap[] */ int *pIndex /* OUT: The index in aMap[] of the match */ ){ int upr, lwr, cnt, m, i; int n = strlen(zName); | > | | | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | ** defined in the page_index.h header file which is automatically ** generated by mkindex.c program. */ static int name_search( const char *zName, /* The name we are looking for */ const NameMap *aMap, /* Search in this array */ int nMap, /* Number of slots in aMap[] */ int iBegin, /* Lower bound on the array search */ int *pIndex /* OUT: The index in aMap[] of the match */ ){ int upr, lwr, cnt, m, i; int n = strlen(zName); lwr = iBegin; upr = nMap-1; while( lwr<=upr ){ int mid, c; mid = (upr+lwr)/2; c = fossil_strcmp(zName, aMap[mid].zName); if( c==0 ){ *pIndex = mid; return 0; }else if( c<0 ){ upr = mid - 1; }else{ lwr = mid + 1; } } for(m=cnt=0, i=upr-2; cnt<2 && i<=upr+3 && i<nMap; i++){ if( i<iBegin ) continue; if( strncmp(zName, aMap[i].zName, n)==0 ){ m = i; cnt++; } } if( cnt==1 ){ *pIndex = m; |
︙ | ︙ | |||
366 367 368 369 370 371 372 373 374 375 376 377 378 379 | cson_value_free(g.json.gc.v); memset(&g.json, 0, sizeof(g.json)); #endif free(g.zErrMsg); if(g.db){ db_close(0); } } /* ** Convert all arguments from mbcs (or unicode) to UTF-8. Then ** search g.argv for arguments "--args FILENAME". If found, then ** (1) remove the two arguments from g.argv ** (2) Read the file FILENAME | > > > > > > > > > > | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 | cson_value_free(g.json.gc.v); memset(&g.json, 0, sizeof(g.json)); #endif free(g.zErrMsg); if(g.db){ db_close(0); } /* ** FIXME: The next two lines cannot always be enabled; however, they ** are very useful for tracking down TH1 memory leaks. */ if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){ if( g.interp ){ Th_DeleteInterp(g.interp); g.interp = 0; } assert( Th_GetOutstandingMalloc()==0 ); } } /* ** Convert all arguments from mbcs (or unicode) to UTF-8. Then ** search g.argv for arguments "--args FILENAME". If found, then ** (1) remove the two arguments from g.argv ** (2) Read the file FILENAME |
︙ | ︙ | |||
387 388 389 390 391 392 393 | Blob file = empty_blob; /* Content of the file */ Blob line = empty_blob; /* One line of the file */ unsigned int nLine; /* Number of lines in the file*/ unsigned int i, j, k; /* Loop counters */ int n; /* Number of bytes in one line */ char *z; /* General use string pointer */ char **newArgv; /* New expanded g.argv under construction */ | | | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 | Blob file = empty_blob; /* Content of the file */ Blob line = empty_blob; /* One line of the file */ unsigned int nLine; /* Number of lines in the file*/ unsigned int i, j, k; /* Loop counters */ int n; /* Number of bytes in one line */ char *z; /* General use string pointer */ char **newArgv; /* New expanded g.argv under construction */ const char *zFileName; /* input file name */ FILE *inFile; /* input FILE */ #if defined(_WIN32) wchar_t buf[MAX_PATH]; #endif g.argc = argc; g.argv = argv; |
︙ | ︙ | |||
486 487 488 489 490 491 492 | zNewArgv[i] = fossil_strdup(argv[i]); } return zNewArgv; } #endif /* | | | | > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | zNewArgv[i] = fossil_strdup(argv[i]); } return zNewArgv; } #endif /* ** Returns a name for a SQLite return code. */ static const char *fossil_sqlite_return_code_name(int rc){ static char zCode[30]; switch( rc & 0xff ){ case SQLITE_OK: return "SQLITE_OK"; case SQLITE_ERROR: return "SQLITE_ERROR"; case SQLITE_INTERNAL: return "SQLITE_INTERNAL"; case SQLITE_PERM: return "SQLITE_PERM"; case SQLITE_ABORT: return "SQLITE_ABORT"; case SQLITE_BUSY: return "SQLITE_BUSY"; case SQLITE_LOCKED: return "SQLITE_LOCKED"; case SQLITE_NOMEM: return "SQLITE_NOMEM"; case SQLITE_READONLY: return "SQLITE_READONLY"; case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT"; case SQLITE_IOERR: return "SQLITE_IOERR"; case SQLITE_CORRUPT: return "SQLITE_CORRUPT"; case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND"; case SQLITE_FULL: return "SQLITE_FULL"; case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN"; case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL"; case SQLITE_EMPTY: return "SQLITE_EMPTY"; case SQLITE_SCHEMA: return "SQLITE_SCHEMA"; case SQLITE_TOOBIG: return "SQLITE_TOOBIG"; case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT"; case SQLITE_MISMATCH: return "SQLITE_MISMATCH"; case SQLITE_MISUSE: return "SQLITE_MISUSE"; case SQLITE_NOLFS: return "SQLITE_NOLFS"; case SQLITE_AUTH: return "SQLITE_AUTH"; case SQLITE_FORMAT: return "SQLITE_FORMAT"; case SQLITE_RANGE: return "SQLITE_RANGE"; case SQLITE_NOTADB: return "SQLITE_NOTADB"; case SQLITE_NOTICE: return "SQLITE_NOTICE"; case SQLITE_WARNING: return "SQLITE_WARNING"; case SQLITE_ROW: return "SQLITE_ROW"; case SQLITE_DONE: return "SQLITE_DONE"; default: { sqlite3_snprintf(sizeof(zCode), zCode, "SQLite return code %d", rc); } } return zCode; } /* Error logs from SQLite */ static void fossil_sqlite_log(void *notUsed, int iCode, const char *zErrmsg){ #ifdef __APPLE__ /* Disable the file alias warning on apple products because Time Machine ** creates lots of aliases and the warning alarms people. */ if( iCode==SQLITE_WARNING ) return; #endif if( iCode==SQLITE_SCHEMA ) return; fossil_warning("%s: %s", fossil_sqlite_return_code_name(iCode), zErrmsg); } /* ** This function attempts to find command line options known to contain ** bitwise flags and initializes the associated global variables. After ** this function executes, all global variables (i.e. in the "g" struct) ** containing option-settable bitwise flag fields must be initialized. */ static void fossil_init_flags_from_options(void){ const char *zValue = find_option("comfmtflags", 0, 1); if( zValue ){ g.comFmtFlags = atoi(zValue); }else{ g.comFmtFlags = COMMENT_PRINT_DEFAULT; } } /* ** This procedure runs first. */ #if defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE) int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */ int wmain(int argc, wchar_t **argv) #else #if defined(_WIN32) int _CRT_glob = 0x0001; /* See MinGW bug #2062 */ #endif int main(int argc, char **argv) #endif { const char *zCmdName = "unknown"; int idx; int rc; if( sqlite3_libversion_number()<3008003 ){ fossil_fatal("Unsuitable SQLite version %s, must be at least 3.8.3", sqlite3_libversion()); } sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); memset(&g, 0, sizeof(g)); g.now = time(0); g.httpHeader = empty_blob; #ifdef FOSSIL_ENABLE_JSON #if defined(NDEBUG) g.json.errorDetailParanoia = 2 /* FIXME: make configurable |
︙ | ︙ | |||
572 573 574 575 576 577 578 579 580 581 582 583 584 585 | expand_args_option(argc, argv); #ifdef FOSSIL_ENABLE_TCL memset(&g.tcl, 0, sizeof(TclContext)); g.tcl.argc = g.argc; g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */ #endif g.mainTimerId = fossil_timer_start(); if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ zCmdName = "cgi"; g.isHTTP = 1; }else if( g.argc<2 ){ fossil_print( "Usage: %s COMMAND ...\n" " or: %s help -- for a list of common commands\n" | > > > > > > > > > > > > > | 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 | expand_args_option(argc, argv); #ifdef FOSSIL_ENABLE_TCL memset(&g.tcl, 0, sizeof(TclContext)); g.tcl.argc = g.argc; g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */ #endif g.mainTimerId = fossil_timer_start(); capture_case_sensitive_option(); g.zVfsName = find_option("vfs",0,1); if( g.zVfsName==0 ){ g.zVfsName = fossil_getenv("FOSSIL_VFS"); } if( g.zVfsName ){ sqlite3_vfs *pVfs = sqlite3_vfs_find(g.zVfsName); if( pVfs ){ sqlite3_vfs_register(pVfs, 1); }else{ fossil_fatal("no such VFS: \"%s\"", g.zVfsName); } } if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ zCmdName = "cgi"; g.isHTTP = 1; }else if( g.argc<2 ){ fossil_print( "Usage: %s COMMAND ...\n" " or: %s help -- for a list of common commands\n" |
︙ | ︙ | |||
601 602 603 604 605 606 607 608 | const char *zChdir = find_option("chdir",0,1); g.isHTTP = 0; g.fQuiet = find_option("quiet", 0, 0)!=0; g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; g.fSqlStats = find_option("sqlstats", 0, 0)!=0; g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; g.fSshTrace = find_option("sshtrace", 0, 0)!=0; if( g.fSqlTrace ) g.fSqlStats = 1; | > > < > > > > > > > > > > > > | > > > > > > > > > > > > | > > > | > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 | const char *zChdir = find_option("chdir",0,1); g.isHTTP = 0; g.fQuiet = find_option("quiet", 0, 0)!=0; g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; g.fSqlStats = find_option("sqlstats", 0, 0)!=0; g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; g.fSshTrace = find_option("sshtrace", 0, 0)!=0; g.fSshClient = 0; g.zSshCmd = 0; if( g.fSqlTrace ) g.fSqlStats = 1; g.fHttpTrace = find_option("httptrace", 0, 0)!=0; #ifdef FOSSIL_ENABLE_TH1_HOOKS g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; #endif g.fAnyTrace = g.fSqlTrace|g.fSystemTrace|g.fSshTrace|g.fHttpTrace; g.zHttpAuth = 0; g.zLogin = find_option("user", "U", 1); g.zSSLIdentity = find_option("ssl-identity", 0, 1); g.zErrlog = find_option("errorlog", 0, 1); fossil_init_flags_from_options(); if( find_option("utc",0,0) ) g.fTimeFormat = 1; if( find_option("localtime",0,0) ) g.fTimeFormat = 2; if( zChdir && file_chdir(zChdir, 0) ){ fossil_fatal("unable to change directories to %s", zChdir); } if( find_option("help",0,0)!=0 ){ /* --help anywhere on the command line is translated into ** "fossil help argv[1] argv[2]..." */ int i; char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) ); for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i]; zNewArgv[i+1] = 0; zNewArgv[0] = g.argv[0]; zNewArgv[1] = "help"; g.argc++; g.argv = zNewArgv; } zCmdName = g.argv[1]; } #ifndef _WIN32 /* There is a bug in stunnel4 in which it sometimes starts up client ** processes without first opening file descriptor 2 (standard error). ** If this happens, and a subsequent open() of a database returns file ** descriptor 2, and then an assert() fires and writes on fd 2, that ** can corrupt the data file. To avoid this problem, make sure open() ** will never return file descriptor 2 or less. */ if( !is_valid_fd(2) ){ int nTry = 0; int fd = 0; int x = 0; do{ fd = open("/dev/null",O_WRONLY); if( fd>=2 ) break; if( fd<0 ) x = errno; }while( nTry++ < 2 ); if( fd<2 ){ g.cgiOutput = 1; g.httpOut = stdout; g.fullHttpReply = !g.isHTTP; fossil_fatal("file descriptor 2 is not open. (fd=%d, errno=%d)", fd, x); } } #endif rc = name_search(zCmdName, aCommand, count(aCommand), FOSSIL_FIRST_CMD, &idx); if( rc==1 ){ #ifdef FOSSIL_ENABLE_TH1_HOOKS if( !g.isHTTP && !g.fNoThHook ){ rc = Th_CommandHook(zCmdName, 0); }else{ rc = TH_OK; } if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ if( rc==TH_OK || rc==TH_RETURN ){ #endif fossil_fatal("%s: unknown command: %s\n" "%s: use \"help\" for more information\n", g.argv[0], zCmdName, g.argv[0]); #ifdef FOSSIL_ENABLE_TH1_HOOKS } if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ Th_CommandNotify(zCmdName, 0); } } fossil_exit(0); #endif }else if( rc==2 ){ int i, n; Blob couldbe; blob_zero(&couldbe); n = strlen(zCmdName); for(i=0; i<count(aCommand); i++){ if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ blob_appendf(&couldbe, " %s", aCommand[i].zName); } } fossil_print("%s: ambiguous command prefix: %s\n" "%s: could be any of:%s\n" "%s: use \"help\" for more information\n", g.argv[0], zCmdName, g.argv[0], blob_str(&couldbe), g.argv[0]); fossil_exit(1); } atexit( fossil_atexit ); #ifdef FOSSIL_ENABLE_TH1_HOOKS /* ** The TH1 return codes from the hook will be handled as follows: ** ** TH_OK: The xFunc() and the TH1 notification will both be executed. ** ** TH_ERROR: The xFunc() will be executed, the TH1 notification will be ** skipped. If the xFunc() is being hooked, the error message ** will be emitted. ** ** TH_BREAK: The xFunc() and the TH1 notification will both be skipped. ** ** TH_RETURN: The xFunc() will be executed, the TH1 notification will be ** skipped. ** ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be ** executed. */ if( !g.isHTTP && !g.fNoThHook ){ rc = Th_CommandHook(aCommand[idx].zName, aCommand[idx].cmdFlags); }else{ rc = TH_OK; } if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ if( rc==TH_OK || rc==TH_RETURN ){ #endif aCommand[idx].xFunc(); #ifdef FOSSIL_ENABLE_TH1_HOOKS } if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ Th_CommandNotify(aCommand[idx].zName, aCommand[idx].cmdFlags); } } #endif fossil_exit(0); /*NOT_REACHED*/ return 0; } /* ** Print a usage comment and quit |
︙ | ︙ | |||
722 723 724 725 726 727 728 729 730 731 732 733 734 735 | zReturn = g.argv[i+hasArg]; remove_from_argv(i, 1+hasArg); break; } } return zReturn; } /* ** Verify that there are no unprocessed command-line options. If ** Any remaining command-line argument begins with "-" print ** an error message and quit. */ void verify_all_options(void){ | > > > > > > > > > > > > > > | 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 | zReturn = g.argv[i+hasArg]; remove_from_argv(i, 1+hasArg); break; } } return zReturn; } /* ** Look for a repository command-line option. If present, [re-]cache it in ** the global state and return the new pointer, freeing any previous value. ** If absent and there is no cached value, return NULL. */ const char *find_repository_option(){ const char *zRepository = find_option("repository", "R", 1); if( zRepository ){ if( g.zRepositoryOption ) fossil_free(g.zRepositoryOption); g.zRepositoryOption = mprintf("%s", zRepository); } return g.zRepositoryOption; } /* ** Verify that there are no unprocessed command-line options. If ** Any remaining command-line argument begins with "-" print ** an error message and quit. */ void verify_all_options(void){ |
︙ | ︙ | |||
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 | } assert(nCmd && "page list is empty?"); multi_column_list(aCmd, nCmd); } /* ** COMMAND: version ** ** Usage: %fossil version ?-verbose|-v? ** ** Print the source code version number for the fossil executable. ** If the verbose option is specified, additional details will ** be output about what optional features this binary was compiled ** with */ void version_cmd(void){ | > > > > > > > > > > > > > > > > > > > > | < | > > > > > | | > > > > | > > > > > > | 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 | } assert(nCmd && "page list is empty?"); multi_column_list(aCmd, nCmd); } /* ** This function returns a human readable version string. */ const char *get_version(){ static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " MANIFEST_DATE " UTC"; return version; } /* ** This function returns the user-agent string for Fossil, for ** use in HTTP(S) requests. */ const char *get_user_agent(){ static const char version[] = "Fossil/" RELEASE_VERSION " (" MANIFEST_DATE " " MANIFEST_VERSION ")"; return version; } /* ** COMMAND: version ** ** Usage: %fossil version ?-verbose|-v? ** ** Print the source code version number for the fossil executable. ** If the verbose option is specified, additional details will ** be output about what optional features this binary was compiled ** with */ void version_cmd(void){ int verboseFlag = 0; fossil_print("This is fossil version %s\n", get_version()); verboseFlag = find_option("verbose","v",0)!=0; /* We should be done with options.. */ verify_all_options(); if(!verboseFlag){ return; }else{ #if defined(FOSSIL_ENABLE_TCL) int rc; const char *zRc; #endif fossil_print("Compiled on %s %s using %s (%d-bit)\n", __DATE__, __TIME__, COMPILER_NAME, sizeof(void*)*8); fossil_print("SQLite %s %.30s\n", sqlite3_libversion(), sqlite3_sourceid()); fossil_print("Schema version %s\n", AUX_SCHEMA_MAX); #if defined(FOSSIL_ENABLE_MINIZ) fossil_print("miniz %s, loaded %s\n", MZ_VERSION, mz_version()); #else fossil_print("zlib %s, loaded %s\n", ZLIB_VERSION, zlibVersion()); #endif #if defined(FOSSIL_ENABLE_SSL) fossil_print("SSL (%s)\n", SSLeay_version(SSLEAY_VERSION)); #endif #if defined(FOSSIL_ENABLE_TH1_DOCS) fossil_print("TH1_DOCS\n"); #endif #if defined(FOSSIL_ENABLE_TH1_HOOKS) fossil_print("TH1_HOOKS\n"); #endif #if defined(FOSSIL_ENABLE_TCL) Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_FORCE_TCL); rc = Th_Eval(g.interp, 0, "tclInvoke info patchlevel", -1); zRc = Th_ReturnCodeName(rc, 0); fossil_print("TCL (Tcl %s, loaded %s: %s)\n", TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0) |
︙ | ︙ | |||
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 | #endif #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) fossil_print("TCL_PRIVATE_STUBS\n"); #endif #if defined(FOSSIL_ENABLE_JSON) fossil_print("JSON (API %s)\n", FOSSIL_JSON_API_VERSION); #endif } } /* ** COMMAND: help ** ** Usage: %fossil help COMMAND | > > > > > | | | | | | | | 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 | #endif #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) fossil_print("TCL_PRIVATE_STUBS\n"); #endif #if defined(FOSSIL_ENABLE_JSON) fossil_print("JSON (API %s)\n", FOSSIL_JSON_API_VERSION); #endif #if defined(BROKEN_MINGW_CMDLINE) fossil_print("MBCS_COMMAND_LINE\n"); #else fossil_print("UNICODE_COMMAND_LINE\n"); #endif } } /* ** COMMAND: help ** ** Usage: %fossil help COMMAND ** or: %fossil COMMAND --help ** ** Display information on how to use COMMAND. To display a list of ** available commands one of: ** ** %fossil help Show common commands ** %fossil help -a|--all Show both common and auxiliary commands ** %fossil help -t|--test Show test commands only ** %fossil help -x|--aux Show auxiliary commands only ** %fossil help -w|--www Show list of WWW pages */ void help_cmd(void){ int rc, idx, isPage = 0; const char *z; const char *zCmdOrPage; const char *zCmdOrPagePlural; if( g.argc<3 ){ z = g.argv[0]; fossil_print( "Usage: %s help COMMAND\n" "Common COMMANDs: (use \"%s help -a|--all\" for a complete list)\n", z, z); command_list(0, CMDFLAG_1ST_TIER); |
︙ | ︙ | |||
906 907 908 909 910 911 912 | if(isPage){ zCmdOrPage = "page"; zCmdOrPagePlural = "pages"; }else{ zCmdOrPage = "command"; zCmdOrPagePlural = "commands"; } | | | 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 | if(isPage){ zCmdOrPage = "page"; zCmdOrPagePlural = "pages"; }else{ zCmdOrPage = "command"; zCmdOrPagePlural = "commands"; } rc = name_search(g.argv[2], aCommand, count(aCommand), 0, &idx); if( rc==1 ){ fossil_print("unknown %s: %s\nAvailable %s:\n", zCmdOrPage, g.argv[2], zCmdOrPagePlural); command_list(0, isPage ? CMDFLAG_WEBPAGE : (0xff & ~CMDFLAG_WEBPAGE)); fossil_exit(1); }else if( rc==2 ){ fossil_print("ambiguous %s prefix: %s\nMatching %s:\n", |
︙ | ︙ | |||
940 941 942 943 944 945 946 | } /* ** WEBPAGE: help ** URL: /help/CMD */ void help_page(void){ | | | | | 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 | } /* ** WEBPAGE: help ** URL: /help/CMD */ void help_page(void){ const char *zCmd = P("cmd"); if( zCmd==0 ) zCmd = P("name"); style_header("Command-line Help"); if( zCmd ){ int rc, idx; char *z, *s, *d; const char *zCmdOrPage = ('/'==*zCmd) ? "page" : "command"; style_submenu_element("Command-List", "Command-List", "%s/help", g.zTop); @ <h1>The "%s(zCmd)" %s(zCmdOrPage):</h1> rc = name_search(zCmd, aCommand, count(aCommand), 0, &idx); if( rc==1 ){ @ unknown command: %s(zCmd) }else if( rc==2 ){ @ ambiguous command prefix: %s(zCmd) }else{ z = (char*)aCmdHelp[idx].zText; if( z==0 ){ |
︙ | ︙ | |||
992 993 994 995 996 997 998 | n = (j+6)/7; for(i=j=0; i<count(aCommand); i++){ const char *z = aCommand[i].zName; if( '/'==*z || strncmp(z,"test",4)==0 ) continue; if( j==0 ){ @ <td valign="top"><ul> } | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 | n = (j+6)/7; for(i=j=0; i<count(aCommand); i++){ const char *z = aCommand[i].zName; if( '/'==*z || strncmp(z,"test",4)==0 ) continue; if( j==0 ){ @ <td valign="top"><ul> } @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li> j++; if( j>=n ){ @ </ul></td> j = 0; } } if( j>0 ){ @ </ul></td> } @ </tr></table> @ <h1>Available web UI pages:</h1> @ (Only pages with help text are linked.) @ <table border="0"><tr> for(i=j=0; i<count(aCommand); i++){ const char *z = aCommand[i].zName; if( '/'!=*z ) continue; j++; } n = (j+4)/5; for(i=j=0; i<count(aCommand); i++){ const char *z = aCommand[i].zName; if( '/'!=*z ) continue; if( j==0 ){ @ <td valign="top"><ul> } if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){ @ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li> }else{ @ <li>%s(z+1)</li> } j++; if( j>=n ){ @ </ul></td> j = 0; } } if( j>0 ){ @ </ul></td> } @ </tr></table> @ <h1>Unsupported commands:</h1> @ <table border="0"><tr> for(i=j=0; i<count(aCommand); i++){ const char *z = aCommand[i].zName; if( strncmp(z,"test",4)!=0 ) continue; j++; } n = (j+3)/4; for(i=j=0; i<count(aCommand); i++){ const char *z = aCommand[i].zName; if( strncmp(z,"test",4)!=0 ) continue; if( j==0 ){ @ <td valign="top"><ul> } if( aCmdHelp[i].zText && *aCmdHelp[i].zText ){ @ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li> }else{ @ <li>%s(z)</li> } j++; if( j>=n ){ @ </ul></td> j = 0; } } if( j>0 ){ |
︙ | ︙ | |||
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 | zMode = PD("HTTPS","off"); zCur = PD("SCRIPT_NAME","/"); i = strlen(zCur); while( i>0 && zCur[i-1]=='/' ) i--; if( fossil_stricmp(zMode,"on")==0 ){ g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur); g.zTop = &g.zBaseURL[8+strlen(zHost)]; }else{ g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur); g.zTop = &g.zBaseURL[7+strlen(zHost)]; } } if( db_is_writeable("repository") ){ if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){ db_multi_exec("INSERT INTO config(name,value,mtime)" "VALUES('baseurl:%q',1,now())", g.zBaseURL); }else{ | > > | 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 | zMode = PD("HTTPS","off"); zCur = PD("SCRIPT_NAME","/"); i = strlen(zCur); while( i>0 && zCur[i-1]=='/' ) i--; if( fossil_stricmp(zMode,"on")==0 ){ g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur); g.zTop = &g.zBaseURL[8+strlen(zHost)]; g.zHttpsURL = g.zBaseURL; }else{ g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur); g.zTop = &g.zBaseURL[7+strlen(zHost)]; g.zHttpsURL = mprintf("https://%s%.*s", zHost, i, zCur); } } if( db_is_writeable("repository") ){ if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){ db_multi_exec("INSERT INTO config(name,value,mtime)" "VALUES('baseurl:%q',1,now())", g.zBaseURL); }else{ |
︙ | ︙ | |||
1139 1140 1141 1142 1143 1144 1145 1146 | ** new repository name. ** ** zRepo might be a directory itself. In that case chroot into ** the directory zRepo. ** ** Assume the user-id and group-id of the repository, or if zRepo ** is a directory, of that directory. */ | > > > | > | | | | | | | | | | | | | | | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 | ** new repository name. ** ** zRepo might be a directory itself. In that case chroot into ** the directory zRepo. ** ** Assume the user-id and group-id of the repository, or if zRepo ** is a directory, of that directory. ** ** The noJail flag means that the chroot jail is not entered. But ** privileges are still lowered to that of the the user-id and group-id. */ static char *enter_chroot_jail(char *zRepo, int noJail){ #if !defined(_WIN32) if( getuid()==0 ){ int i; struct stat sStat; Blob dir; char *zDir; if( g.db!=0 ){ db_close(1); } file_canonical_name(zRepo, &dir, 0); zDir = blob_str(&dir); if( !noJail ){ if( file_isdir(zDir)==1 ){ if( file_chdir(zDir, 1) ){ fossil_fatal("unable to chroot into %s", zDir); } zRepo = "/"; }else{ for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} if( zDir[i]!='/' ) fossil_fatal("bad repository name: %s", zRepo); if( i>0 ){ zDir[i] = 0; if( file_chdir(zDir, 1) ){ fossil_fatal("unable to chroot into %s", zDir); } zDir[i] = '/'; } zRepo = &zDir[i]; } } if( stat(zRepo, &sStat)!=0 ){ fossil_fatal("cannot stat() repository: %s", zRepo); } i = setgid(sStat.st_gid); i = i || setuid(sStat.st_uid); if(i){ fossil_fatal("setgid/uid() failed with errno %d", errno); } if( g.db==0 && file_isfile(zRepo) ){ db_open_repository(zRepo); } } #endif return zRepo; } /* ** Generate a web-page that lists all repositories located under the ** g.zRepositoryName directory and return non-zero. ** ** Or, if no repositories can be located beneath g.zRepositoryName, ** return 0. */ static int repo_list_page(void){ Blob base; int n = 0; assert( g.db==0 ); blob_init(&base, g.zRepositoryName, -1); sqlite3_open(":memory:", &g.db); db_multi_exec("CREATE TABLE sfile(x TEXT);"); db_multi_exec("CREATE TABLE vfile(pathname);"); vfile_scan(&base, blob_size(&base), 0, 0, 0); db_multi_exec("DELETE FROM sfile WHERE x NOT GLOB '*.fossil'"); n = db_int(0, "SELECT count(*) FROM sfile"); if( n>0 ){ Stmt q; @ <h1>Available Repositories:</h1> @ <ol> db_prepare(&q, "SELECT x, substr(x,-7,-100000)||'/home'" " FROM sfile ORDER BY x COLLATE nocase;"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zUrl = db_column_text(&q, 1); @ <li><a href="%h(zUrl)">%h(zName)</a></li> } @ </ol> cgi_reply(); } sqlite3_close(g.db); g.db = 0; return n; } /* ** Preconditions: ** ** * Environment variables are set up according to the CGI standard. ** ** If the repository is known, it has already been opened. If unknown, |
︙ | ︙ | |||
1205 1206 1207 1208 1209 1210 1211 | ** Or, if an ordinary file named $prefix is found, and $prefix matches ** pFileGlob and $prefix does not match "*.fossil*" and the mimetype of ** $prefix can be determined from its suffix, then the file $prefix is ** returned as static text. ** ** If no suitable webpage is found, try to redirect to zNotFound. */ | | > > > > | 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 | ** Or, if an ordinary file named $prefix is found, and $prefix matches ** pFileGlob and $prefix does not match "*.fossil*" and the mimetype of ** $prefix can be determined from its suffix, then the file $prefix is ** returned as static text. ** ** If no suitable webpage is found, try to redirect to zNotFound. */ static void process_one_web_page( const char *zNotFound, /* Redirect here on a 404 if not NULL */ Glob *pFileGlob, /* Deliver static files matching */ int allowRepoList /* Send repo list for "/" URL */ ){ const char *zPathInfo; char *zPath = NULL; int idx; int i; /* If the repository has not been opened already, then find the ** repository based on the first element of PATH_INFO and open it. |
︙ | ︙ | |||
1249 1250 1251 1252 1253 1254 1255 | } szFile = 1; break; } if( szFile==0 ){ if( zRepo[0]=='/' && zRepo[1]=='/' ){ zRepo++; j--; } szFile = file_size(zRepo); | > > > > > | > | | > > > > | 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 | } szFile = 1; break; } if( szFile==0 ){ if( zRepo[0]=='/' && zRepo[1]=='/' ){ zRepo++; j--; } szFile = file_size(zRepo); /* this should only be set from the --baseurl option, not CGI */ if( g.zBaseURL && g.zBaseURL[0]!=0 && g.zTop && g.zTop[0]!=0 && file_isdir(g.zRepositoryName)==1 ){ g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); g.zTop = mprintf("%s%.*s", g.zTop, i, zPathInfo); } } if( szFile<0 && i>0 ){ const char *zMimetype; assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); zRepo[j] = 0; if( zPathInfo[i]=='/' && file_isdir(zRepo)==1 ){ fossil_free(zToFree); i++; continue; } if( pFileGlob!=0 && file_isfile(zRepo) && glob_match(pFileGlob, zRepo) && sqlite3_strglob("*.fossil*",zRepo)!=0 && (zMimetype = mimetype_from_name(zRepo))!=0 && strcmp(zMimetype, "application/x-fossil-artifact")!=0 ){ Blob content; blob_read_from_file(&content, zRepo); cgi_set_content_type(zMimetype); cgi_set_content(&content); cgi_reply(); return; } zRepo[j] = '.'; } if( szFile<1024 ){ set_base_url(0); if( zNotFound ){ cgi_redirect(zNotFound); }else if( strcmp(zPathInfo,"/")==0 && allowRepoList && repo_list_page() ){ /* Will return a list of repositories */ }else{ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); return; } #endif |
︙ | ︙ | |||
1312 1313 1314 1315 1316 1317 1318 | zRepo, zPathInfo, zNewScript); } } /* Find the page that the user has requested, construct and deliver that ** page. */ | > | | 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 | zRepo, zPathInfo, zNewScript); } } /* Find the page that the user has requested, construct and deliver that ** page. */ if( g.zContentType && strncmp(g.zContentType, "application/x-fossil", 20)==0 ){ zPathInfo = "/xfer"; } set_base_url(0); if( zPathInfo==0 || zPathInfo[0]==0 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ |
︙ | ︙ | |||
1362 1363 1364 1365 1366 1367 1368 | for(jj=0; zAltRepo[jj] && zAltRepo[jj]!=':'; jj++){} if( zAltRepo[jj]==':' ){ zAltRepo[jj] = 0; zAltRepo += jj+1; }else{ zUser = "nobody"; } | | | 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 | for(jj=0; zAltRepo[jj] && zAltRepo[jj]!=':'; jj++){} if( zAltRepo[jj]==':' ){ zAltRepo[jj] = 0; zAltRepo += jj+1; }else{ zUser = "nobody"; } if( g.zLogin==0 || g.zLogin[0]==0 ) zUser = "nobody"; if( zAltRepo[0]!='/' ){ zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo); file_simplify_name(zAltRepo, -1, 0); } db_close(1); db_open_repository(zAltRepo); login_as_user(zUser); |
︙ | ︙ | |||
1418 1419 1420 1421 1422 1423 1424 | } #endif } /* Locate the method specified by the path and execute the function ** that implements that method. */ | | < > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | | | | 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 | } #endif } /* Locate the method specified by the path and execute the function ** that implements that method. */ if( name_search(g.zPath, aWebpage, count(aWebpage), 0, &idx) ){ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0); }else #endif { #ifdef FOSSIL_ENABLE_TH1_HOOKS int rc; if( !g.fNoThHook ){ rc = Th_WebpageHook(g.zPath, 0); }else{ rc = TH_OK; } if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ if( rc==TH_OK || rc==TH_RETURN ){ #endif cgi_set_status(404,"Not Found"); @ <h1>Not Found</h1> @ <p>Page not found: %h(g.zPath)</p> #ifdef FOSSIL_ENABLE_TH1_HOOKS } if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ Th_WebpageNotify(g.zPath, 0); } } #endif } }else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0); }else #endif { @ <h1>Server Configuration Error</h1> @ <p>The database schema on the server is out-of-date. Please ask @ the administrator to run <b>fossil rebuild</b>.</p> } }else{ #ifdef FOSSIL_ENABLE_TH1_HOOKS /* ** The TH1 return codes from the hook will be handled as follows: ** ** TH_OK: The xFunc() and the TH1 notification will both be executed. ** ** TH_ERROR: The xFunc() will be executed, the TH1 notification will be ** skipped. If the xFunc() is being hooked, the error message ** will be emitted. ** ** TH_BREAK: The xFunc() and the TH1 notification will both be skipped. ** ** TH_RETURN: The xFunc() will be executed, the TH1 notification will be ** skipped. ** ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be ** executed. */ int rc; if( !g.fNoThHook ){ rc = Th_WebpageHook(aWebpage[idx].zName, aWebpage[idx].cmdFlags); }else{ rc = TH_OK; } if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ if( rc==TH_OK || rc==TH_RETURN ){ #endif aWebpage[idx].xFunc(); #ifdef FOSSIL_ENABLE_TH1_HOOKS } if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ Th_WebpageNotify(aWebpage[idx].zName, aWebpage[idx].cmdFlags); } } #endif } /* Return the result. */ cgi_reply(); } /* If the CGI program contains one or more lines of the form ** ** redirect: repository-filename http://hostname/path/%s ** ** then control jumps here. Search each repository for an artifact ID ** or ticket ID that matches the "name" CGI parameter and for the ** first match, redirect to the corresponding URL with the "name" CGI ** parameter inserted. Paint an error page if no match is found. ** ** If there is a line of the form: ** ** redirect: * URL ** ** Then a redirect is made to URL if no match is found. Otherwise a ** very primitive error message is returned. |
︙ | ︙ | |||
1482 1483 1484 1485 1486 1487 1488 | if( zName && validate16(zName, strlen(zName)) ){ for(i=0; i<nRedirect; i++){ if( fossil_strcmp(azRedirect[i*2],"*")==0 ){ zNotFound = azRedirect[i*2+1]; continue; } db_open_repository(azRedirect[i*2]); | | > | | | 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 | if( zName && validate16(zName, strlen(zName)) ){ for(i=0; i<nRedirect; i++){ if( fossil_strcmp(azRedirect[i*2],"*")==0 ){ zNotFound = azRedirect[i*2+1]; continue; } db_open_repository(azRedirect[i*2]); if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'", zName) || db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ cgi_redirectf(azRedirect[i*2+1] /*works-like:"%s"*/, zName); return; } db_close(1); } } if( zNotFound ){ cgi_redirectf(zNotFound /*works-like:"%s"*/, zName); }else{ @ <html> @ <head><title>No Such Object</title></head> @ <body> @ <p>No such object: <b>%h(zName)</b></p> @ </body> cgi_reply(); |
︙ | ︙ | |||
1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 | */ void cmd_cgi(void){ const char *zFile; const char *zNotFound = 0; char **azRedirect = 0; /* List of repositories to redirect to */ int nRedirect = 0; /* Number of entries in azRedirect */ Glob *pFileGlob = 0; /* Pattern for files */ Blob config, line, key, value, value2; if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){ zFile = g.argv[2]; }else{ zFile = g.argv[1]; } g.httpOut = stdout; g.httpIn = stdin; fossil_binary_mode(g.httpOut); fossil_binary_mode(g.httpIn); g.cgiOutput = 1; blob_read_from_file(&config, zFile); while( blob_line(&config, &line) ){ if( !blob_token(&line, &key) ) continue; if( blob_buffer(&key)[0]=='#' ) continue; | > < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | < < < | > > > | | > > > > > > > > > > > > > > > | | > > | | | 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 | */ void cmd_cgi(void){ const char *zFile; const char *zNotFound = 0; char **azRedirect = 0; /* List of repositories to redirect to */ int nRedirect = 0; /* Number of entries in azRedirect */ Glob *pFileGlob = 0; /* Pattern for files */ int allowRepoList = 0; /* Allow lists of repository files */ Blob config, line, key, value, value2; if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){ zFile = g.argv[2]; }else{ zFile = g.argv[1]; } g.httpOut = stdout; g.httpIn = stdin; fossil_binary_mode(g.httpOut); fossil_binary_mode(g.httpIn); g.cgiOutput = 1; blob_read_from_file(&config, zFile); while( blob_line(&config, &line) ){ if( !blob_token(&line, &key) ) continue; if( blob_buffer(&key)[0]=='#' ) continue; if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){ /* repository: FILENAME ** ** The name of the Fossil repository to be served via CGI. Most ** fossil CGI scripts have a single non-comment line that contains ** this one entry. */ blob_trim(&value); db_open_repository(blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){ /* directory: DIRECTORY ** ** If repository: is omitted, then terms of the PATH_INFO cgi parameter ** are appended to DIRECTORY looking for a repository (whose name ends ** in ".fossil") or a file in "files:". */ db_close(1); g.zRepositoryName = mprintf("%s", blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){ /* notfound: URL ** ** If using directory: and no suitable repository or file is found, ** then redirect to URL. */ zNotFound = mprintf("%s", blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "localauth") ){ /* localauth ** ** Grant "administrator" privileges to users connecting with HTTP ** from IP address 127.0.0.1. Do not bother checking credentials. */ g.useLocalauth = 1; continue; } if( blob_eq(&key, "repolist") ){ /* repolist ** ** If using "directory:" and the URL is "/" then generate a page ** showing a list of available repositories. */ allowRepoList = 1; continue; } if( blob_eq(&key, "redirect:") && blob_token(&line, &value) && blob_token(&line, &value2) ){ /* See the header comment on the redirect_web_page() function ** above for details. */ nRedirect++; azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*)); azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value)); azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2)); blob_reset(&value); blob_reset(&value2); continue; } if( blob_eq(&key, "files:") && blob_token(&line, &value) ){ /* files: GLOBLIST ** ** GLOBLIST is a comma-separated list of filename globs. For ** example: *.html,*.css,*.js ** ** If the repository: line is omitted and then PATH_INFO is searched ** for files that match any of these GLOBs and if any such file is ** found it is returned verbatim. This feature allows "fossil server" ** to function as a primitive web-server delivering arbitrary content. */ pFileGlob = glob_create(blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "setenv:") && blob_token(&line, &value) && blob_token(&line, &value2) ){ /* setenv: NAME VALUE ** ** Sets environment variable NAME to VALUE */ fossil_setenv(blob_str(&value), blob_str(&value2)); blob_reset(&value); blob_reset(&value2); continue; } if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){ /* debug: FILENAME ** ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go ** into FILENAME. */ g.fDebug = fossil_fopen(blob_str(&value), "ab"); blob_reset(&value); continue; } if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ /* errorlog: FILENAME ** ** Causes messages from warnings, errors, and panics to be appended ** to FILENAME. */ g.zErrlog = mprintf("%s", blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){ /* HOME: VALUE ** ** Set CGI parameter "HOME" to VALUE. This is legacy. Use ** setenv: instead. */ cgi_setenv("HOME", blob_str(&value)); blob_reset(&value); continue; } if( blob_eq(&key, "skin:") && blob_token(&line, &value) ){ /* skin: LABEL ** ** Use one of the built-in skins defined by LABEL. LABEL is the ** name of the subdirectory under the skins/ directory that holds ** the elements of the built-in skin. If LABEL does not match, ** this directive is a silent no-op. */ skin_use_alternative(blob_str(&value)); blob_reset(&value); continue; } } blob_reset(&config); if( g.db==0 && g.zRepositoryName==0 && nRedirect==0 ){ cgi_panic("Unable to find or open the project repository"); } cgi_init(); if( nRedirect ){ redirect_web_page(nRedirect, azRedirect); }else{ process_one_web_page(zNotFound, pFileGlob, allowRepoList); } } /* ** If g.argv[arg] exists then it is either the name of a repository ** that will be used by a server, or else it is a directory that ** contains multiple repositories that can be served. If g.argv[arg] ** is a directory, the repositories it contains must be named ** "*.fossil". If g.argv[arg] does not exists, then we must be within ** a check-out and the repository to be served is the repository of ** that check-out. ** ** Open the repository to be served if it is known. If g.argv[arg] is ** a directory full of repositories, then set g.zRepositoryName to ** the name of that directory and the specific repository will be ** opened later by process_one_web_page() based on the content of ** the PATH_INFO variable. ** ** If the fCreate flag is set, then create the repository if it ** does not already exist. */ static void find_server_repository(int arg, int fCreate){ if( g.argc<=arg ){ db_must_be_within_tree(); }else{ const char *zRepo = g.argv[arg]; int isDir = file_isdir(zRepo); if( isDir==1 ){ g.zRepositoryName = mprintf("%s", zRepo); file_simplify_name(g.zRepositoryName, -1, 0); }else{ if( isDir==0 && fCreate ){ const char *zPassword; db_create_repository(zRepo); db_open_repository(zRepo); db_begin_transaction(); db_initial_setup(0, "now", g.zLogin); db_end_transaction(0); fossil_print("project-id: %s\n", db_get("project-code", 0)); fossil_print("server-id: %s\n", db_get("server-code", 0)); zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); cache_initialize(); g.zLogin = 0; g.userUid = 0; }else{ db_open_repository(zRepo); } } } } /* ** undocumented format: ** ** fossil http INFILE OUTFILE IPADDR ?REPOSITORY? ** ** The argv==6 form is used by the win32 server only. ** ** COMMAND: http* ** ** Usage: %fossil http ?REPOSITORY? ?OPTIONS? ** ** Handle a single HTTP request appearing on stdin. The resulting webpage ** is delivered on stdout. This method is used to launch an HTTP request ** handler from inetd, for example. The argument is the name of the ** repository. ** ** If REPOSITORY is a directory that contains one or more repositories, |
︙ | ︙ | |||
1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 | ** thus also no redirecting from http: to https: will take place. ** ** If the --localauth option is given, then automatic login is performed ** for requests coming from localhost, if the "localauth" setting is not ** enabled. ** ** Options: ** --localauth enable automatic login for local connections ** --host NAME specify hostname of the server ** --https signal a request coming in via https ** --nossl signal that no SSL connections are available ** --notfound URL use URL as "HTTP 404, object not found" page. | > > > < | > | > > > > > | > > > > > > > | | | | | > > > > | > > | < > | > > | > > > > > > > > > > > > > > > > < | > > > > > > | | > | 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 | ** thus also no redirecting from http: to https: will take place. ** ** If the --localauth option is given, then automatic login is performed ** for requests coming from localhost, if the "localauth" setting is not ** enabled. ** ** Options: ** --baseurl URL base URL (useful with reverse proxies) ** --files GLOB comma-separate glob patterns for static file to serve ** --localauth enable automatic login for local connections ** --host NAME specify hostname of the server ** --https signal a request coming in via https ** --nojail drop root privilege but do not enter the chroot jail ** --nossl signal that no SSL connections are available ** --notfound URL use URL as "HTTP 404, object not found" page. ** --repolist If REPOSITORY is directory, URL "/" lists all repos ** --scgi Interpret input as SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** ** See also: cgi, server, winsrv */ void cmd_http(void){ const char *zIpAddr = 0; const char *zNotFound; const char *zHost; const char *zAltBase; const char *zFileGlob; int useSCGI; int noJail; int allowRepoList; /* The winhttp module passes the --files option as --files-urlenc with ** the argument being URL encoded, to avoid wildcard expansion in the ** shell. This option is for internal use and is undocumented. */ zFileGlob = find_option("files-urlenc",0,1); if( zFileGlob ){ char *z = mprintf("%s", zFileGlob); dehttpize(z); zFileGlob = z; }else{ zFileGlob = find_option("files",0,1); } skin_override(); zNotFound = find_option("notfound", 0, 1); noJail = find_option("nojail",0,0)!=0; allowRepoList = find_option("repolist",0,0)!=0; g.useLocalauth = find_option("localauth", 0, 0)!=0; g.sslNotAvailable = find_option("nossl", 0, 0)!=0; useSCGI = find_option("scgi", 0, 0)!=0; zAltBase = find_option("baseurl", 0, 1); if( zAltBase ) set_base_url(zAltBase); if( find_option("https",0,0)!=0 ){ zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */ cgi_replace_parameter("HTTPS","on"); } zHost = find_option("host", 0, 1); if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); g.cgiOutput = 1; /* We should be done with options.. */ verify_all_options(); if( g.argc!=2 && g.argc!=3 && g.argc!=5 && g.argc!=6 ){ fossil_fatal("no repository specified"); } g.fullHttpReply = 1; if( g.argc>=5 ){ g.httpIn = fossil_fopen(g.argv[2], "rb"); g.httpOut = fossil_fopen(g.argv[3], "wb"); zIpAddr = g.argv[4]; find_server_repository(5, 0); }else{ g.httpIn = stdin; g.httpOut = stdout; find_server_repository(2, 0); } if( zIpAddr==0 ){ zIpAddr = cgi_ssh_remote_addr(0); if( zIpAddr && zIpAddr[0] ){ g.fSshClient |= CGI_SSH_CLIENT; } } g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail); if( useSCGI ){ cgi_handle_scgi_request(); }else if( g.fSshClient & CGI_SSH_CLIENT ){ ssh_request_loop(zIpAddr, glob_create(zFileGlob)); }else{ cgi_handle_http_request(zIpAddr); } process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList); } /* ** Process all requests in a single SSH connection if possible. */ void ssh_request_loop(const char *zIpAddr, Glob *FileGlob){ blob_zero(&g.cgiIn); do{ cgi_handle_ssh_http_request(zIpAddr); process_one_web_page(0, FileGlob, 0); blob_reset(&g.cgiIn); } while ( g.fSshClient & CGI_SSH_FOSSIL || g.fSshClient & CGI_SSH_COMPAT ); } /* ** Note that the following command is used by ssh:// processing. ** ** COMMAND: test-http ** Works like the http command but gives setup permission to all users. ** */ void cmd_test_http(void){ const char *zIpAddr; /* IP address of remote client */ Th_InitTraceLog(); login_set_capabilities("sx", 0); g.useLocalauth = 1; g.httpIn = stdin; g.httpOut = stdout; find_server_repository(2, 0); g.cgiOutput = 1; g.fullHttpReply = 1; zIpAddr = cgi_ssh_remote_addr(0); if( zIpAddr && zIpAddr[0] ){ g.fSshClient |= CGI_SSH_CLIENT; ssh_request_loop(zIpAddr, 0); }else{ cgi_set_parameter("REMOTE_ADDR", "127.0.0.1"); cgi_handle_http_request(0); process_one_web_page(0, 0, 0); } } #if !defined(_WIN32) #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__) /* ** Search for an executable on the PATH environment variable. ** Return true (1) if found and false (0) if not found. |
︙ | ︙ | |||
1803 1804 1805 1806 1807 1808 1809 | ** within an open checkout. ** ** The "ui" command automatically starts a web browser after initializing ** the web server. The "ui" command also binds to 127.0.0.1 and so will ** only process HTTP traffic from the local machine. ** ** The REPOSITORY can be a directory (aka folder) that contains one or | | | | | | | < | > > > > > < | < > > > > > > > | > > > > > > > > > > > > > | > > > > | > | | 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 | ** within an open checkout. ** ** The "ui" command automatically starts a web browser after initializing ** the web server. The "ui" command also binds to 127.0.0.1 and so will ** only process HTTP traffic from the local machine. ** ** The REPOSITORY can be a directory (aka folder) that contains one or ** more repositories with names ending in ".fossil". In this case, a ** prefix of the URL pathname is used to search the directory for an ** appropriate repository. To thwart mischief, the pathname in the URL must ** contain only alphanumerics, "_", "/", "-", and ".", and no "-" may ** occur after "/", and every "." must be surrounded on both sides by ** alphanumerics. Any pathname that does not satisfy these constraints ** results in a 404 error. Files in REPOSITORY that match the comma-separated ** list of glob patterns given by --files and that have known suffixes ** such as ".txt" or ".html" or ".jpeg" and do not match the pattern ** "*.fossil*" will be served as static content. With the "ui" command, ** the REPOSITORY can only be a directory if the --notfound option is ** also present. ** ** By default, the "ui" command provides full administrative access without ** having to log in. This can be disabled by turning off the "localauth" ** setting. Automatic login for the "server" command is available if the ** --localauth option is present and the "localauth" setting is off and the ** connection is from localhost. The "ui" command also enables --repolist ** by default. ** ** Options: ** --baseurl URL Use URL as the base (useful for reverse proxies) ** --create Create a new REPOSITORY if it does not already exist ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --localauth enable automatic login for requests from localhost ** --localhost listen on 127.0.0.1 only (always true for "ui") ** --nojail Drop root privileges but do not enter the chroot jail ** --notfound URL Redirect ** -P|--port TCPPORT listen to request on port TCPPORT ** --th-trace trace TH1 execution (for debugging purposes) ** --repolist If REPOSITORY is dir, URL "/" lists repos. ** --scgi Accept SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** ** See also: cgi, http, winsrv */ void cmd_webserver(void){ int iPort, mxPort; /* Range of TCP ports allowed */ const char *zPort; /* Value of the --port option */ const char *zBrowser; /* Name of web browser program */ char *zBrowserCmd = 0; /* Command to launch the web browser */ int isUiCmd; /* True if command is "ui", not "server' */ const char *zNotFound; /* The --notfound option or NULL */ int flags = 0; /* Server flags */ #if !defined(_WIN32) int noJail; /* Do not enter the chroot jail */ #endif int allowRepoList; /* List repositories on URL "/" */ const char *zAltBase; /* Argument to the --baseurl option */ const char *zFileGlob; /* Static content must match this */ char *zIpAddr = 0; /* Bind to this IP address */ int fCreate = 0; #if defined(_WIN32) const char *zStopperFile; /* Name of file used to terminate server */ zStopperFile = find_option("stopper", 0, 1); #endif zFileGlob = find_option("files-urlenc",0,1); if( zFileGlob ){ char *z = mprintf("%s", zFileGlob); dehttpize(z); zFileGlob = z; }else{ zFileGlob = find_option("files",0,1); } skin_override(); #if !defined(_WIN32) noJail = find_option("nojail",0,0)!=0; #endif g.useLocalauth = find_option("localauth", 0, 0)!=0; Th_InitTraceLog(); zPort = find_option("port", "P", 1); zNotFound = find_option("notfound", 0, 1); allowRepoList = find_option("repolist",0,0)!=0; zAltBase = find_option("baseurl", 0, 1); fCreate = find_option("create",0,0)!=0; if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI; if( zAltBase ){ set_base_url(zAltBase); } if( find_option("localhost", 0, 0)!=0 ){ flags |= HTTP_SERVER_LOCALHOST; } /* We should be done with options.. */ verify_all_options(); if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); isUiCmd = g.argv[1][0]=='u'; if( isUiCmd ){ flags |= HTTP_SERVER_LOCALHOST|HTTP_SERVER_REPOLIST; g.useLocalauth = 1; allowRepoList = 1; } find_server_repository(2, fCreate); if( zPort ){ int i; for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){} if( i>0 ){ zIpAddr = mprintf("%.*s", i, zPort); zPort += i+1; } |
︙ | ︙ | |||
1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 | zBrowser = db_get("web-browser", "open"); #endif if( zIpAddr ){ zBrowserCmd = mprintf("%s http://%s:%%d/ &", zBrowser, zIpAddr); }else{ zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); } } db_close(1); if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){ fossil_fatal("unable to listen on TCP socket %d", iPort); } g.sslNotAvailable = 1; g.httpIn = stdin; g.httpOut = stdout; if( g.fHttpTrace || g.fSqlTrace ){ fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); } g.cgiOutput = 1; | > > | | | > > > > > | 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 | zBrowser = db_get("web-browser", "open"); #endif if( zIpAddr ){ zBrowserCmd = mprintf("%s http://%s:%%d/ &", zBrowser, zIpAddr); }else{ zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); } if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY; if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT; } db_close(1); if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){ fossil_fatal("unable to listen on TCP socket %d", iPort); } g.sslNotAvailable = 1; g.httpIn = stdin; g.httpOut = stdout; if( g.fHttpTrace || g.fSqlTrace ){ fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); } g.cgiOutput = 1; find_server_repository(2, 0); g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail); if( flags & HTTP_SERVER_SCGI ){ cgi_handle_scgi_request(); }else{ cgi_handle_http_request(0); } process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList); #else /* Win32 implementation */ if( isUiCmd ){ zBrowser = db_get("web-browser", "start"); if( zIpAddr ){ zBrowserCmd = mprintf("%s http://%s:%%d/ &", zBrowser, zIpAddr); }else{ zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); } if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY; if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT; } db_close(1); if( allowRepoList ){ flags |= HTTP_SERVER_REPOLIST; } if( win32_http_service(iPort, zNotFound, zFileGlob, flags) ){ win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zNotFound, zFileGlob, zIpAddr, flags); } #endif } |
︙ | ︙ |
Changes to src/main.mk.
1 2 3 4 5 6 7 8 9 10 11 12 | # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl") ############################################################################## # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # # This file is included by primary Makefile. # | | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl") ############################################################################## # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # # This file is included by primary Makefile. # XTCC = $(TCC) -I. -I$(SRCDIR) -I$(OBJDIR) $(TCCFLAGS) $(CFLAGS) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/builtin.c \ $(SRCDIR)/bundle.c \ $(SRCDIR)/cache.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/comformat.c \ |
︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 51 52 | $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ | > > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ |
︙ | ︙ | |||
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | $(SRCDIR)/json_report.c \ $(SRCDIR)/json_status.c \ $(SRCDIR)/json_tag.c \ $(SRCDIR)/json_timeline.c \ $(SRCDIR)/json_user.c \ $(SRCDIR)/json_wiki.c \ $(SRCDIR)/leaf.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/path.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ $(SRCDIR)/tar.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ $(SRCDIR)/unicode.c \ $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | $(SRCDIR)/json_report.c \ $(SRCDIR)/json_status.c \ $(SRCDIR)/json_tag.c \ $(SRCDIR)/json_timeline.c \ $(SRCDIR)/json_user.c \ $(SRCDIR)/json_wiki.c \ $(SRCDIR)/leaf.c \ $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/path.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/publish.c \ $(SRCDIR)/purge.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ $(SRCDIR)/tar.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ $(SRCDIR)/unicode.c \ $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ $(SRCDIR)/../skins/aht/details.txt \ $(SRCDIR)/../skins/black_and_white/css.txt \ $(SRCDIR)/../skins/black_and_white/details.txt \ $(SRCDIR)/../skins/black_and_white/footer.txt \ $(SRCDIR)/../skins/black_and_white/header.txt \ $(SRCDIR)/../skins/blitz/css.txt \ $(SRCDIR)/../skins/blitz/details.txt \ $(SRCDIR)/../skins/blitz/footer.txt \ $(SRCDIR)/../skins/blitz/header.txt \ $(SRCDIR)/../skins/blitz/ticket.txt \ $(SRCDIR)/../skins/blitz_no_logo/css.txt \ $(SRCDIR)/../skins/blitz_no_logo/details.txt \ $(SRCDIR)/../skins/blitz_no_logo/footer.txt \ $(SRCDIR)/../skins/blitz_no_logo/header.txt \ $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \ $(SRCDIR)/../skins/default/css.txt \ $(SRCDIR)/../skins/default/details.txt \ $(SRCDIR)/../skins/default/footer.txt \ $(SRCDIR)/../skins/default/header.txt \ $(SRCDIR)/../skins/eagle/css.txt \ $(SRCDIR)/../skins/eagle/details.txt \ $(SRCDIR)/../skins/eagle/footer.txt \ $(SRCDIR)/../skins/eagle/header.txt \ $(SRCDIR)/../skins/enhanced1/css.txt \ $(SRCDIR)/../skins/enhanced1/details.txt \ $(SRCDIR)/../skins/enhanced1/footer.txt \ $(SRCDIR)/../skins/enhanced1/header.txt \ $(SRCDIR)/../skins/khaki/css.txt \ $(SRCDIR)/../skins/khaki/details.txt \ $(SRCDIR)/../skins/khaki/footer.txt \ $(SRCDIR)/../skins/khaki/header.txt \ $(SRCDIR)/../skins/original/css.txt \ $(SRCDIR)/../skins/original/details.txt \ $(SRCDIR)/../skins/original/footer.txt \ $(SRCDIR)/../skins/original/header.txt \ $(SRCDIR)/../skins/plain_gray/css.txt \ $(SRCDIR)/../skins/plain_gray/details.txt \ $(SRCDIR)/../skins/plain_gray/footer.txt \ $(SRCDIR)/../skins/plain_gray/header.txt \ $(SRCDIR)/../skins/rounded1/css.txt \ $(SRCDIR)/../skins/rounded1/details.txt \ $(SRCDIR)/../skins/rounded1/footer.txt \ $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/../skins/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/markdown.md TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/builtin_.c \ $(OBJDIR)/bundle_.c \ $(OBJDIR)/cache_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ |
︙ | ︙ | |||
148 149 150 151 152 153 154 155 156 157 158 159 160 161 | $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ | > > | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ |
︙ | ︙ | |||
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | $(OBJDIR)/json_report_.c \ $(OBJDIR)/json_status_.c \ $(OBJDIR)/json_tag_.c \ $(OBJDIR)/json_timeline_.c \ $(OBJDIR)/json_user_.c \ $(OBJDIR)/json_wiki_.c \ $(OBJDIR)/leaf_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/pivot_.c \ $(OBJDIR)/popen_.c \ $(OBJDIR)/pqueue_.c \ $(OBJDIR)/printf_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ $(OBJDIR)/tar_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ $(OBJDIR)/unicode_.c \ $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ | > > > > > > > > > | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | $(OBJDIR)/json_report_.c \ $(OBJDIR)/json_status_.c \ $(OBJDIR)/json_tag_.c \ $(OBJDIR)/json_timeline_.c \ $(OBJDIR)/json_user_.c \ $(OBJDIR)/json_wiki_.c \ $(OBJDIR)/leaf_.c \ $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/pivot_.c \ $(OBJDIR)/popen_.c \ $(OBJDIR)/pqueue_.c \ $(OBJDIR)/printf_.c \ $(OBJDIR)/publish_.c \ $(OBJDIR)/purge_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ $(OBJDIR)/tar_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ $(OBJDIR)/unicode_.c \ $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/builtin.o \ $(OBJDIR)/bundle.o \ $(OBJDIR)/cache.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ |
︙ | ︙ | |||
257 258 259 260 261 262 263 264 265 266 267 268 269 270 | $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ | > > | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ |
︙ | ︙ | |||
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | $(OBJDIR)/json_report.o \ $(OBJDIR)/json_status.o \ $(OBJDIR)/json_tag.o \ $(OBJDIR)/json_timeline.o \ $(OBJDIR)/json_user.o \ $(OBJDIR)/json_wiki.o \ $(OBJDIR)/leaf.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/path.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ $(OBJDIR)/tar.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ $(OBJDIR)/unicode.o \ $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o APPNAME = fossil$(E) all: $(OBJDIR) $(APPNAME) install: $(APPNAME) mkdir -p $(INSTALLDIR) mv $(APPNAME) $(INSTALLDIR) $(OBJDIR): -mkdir $(OBJDIR) $(OBJDIR)/translate: $(SRCDIR)/translate.c $(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c $(OBJDIR)/mkversion: $(SRCDIR)/mkversion.c $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c # WARNING. DANGER. Running the test suite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set # to 1. If it is set to 1, then there is no need to build or link | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < > | | > | > | > > > > > > > | > | | > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | > | | > | | > | | > | | > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > > > > > > > > > > > > > > > > > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > > > > > > > > > | | > | | > | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > > > > > > > > > > > > > > > > > | | > | | > | | > | | > | | > | | > | | > | | > | | > > > > > > > > > | | > | | > | | > | | > > > > > > > > > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > > > > > > > > > | | > | | > | | > | | > | | > | | | | | > > > | | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 | $(OBJDIR)/json_report.o \ $(OBJDIR)/json_status.o \ $(OBJDIR)/json_tag.o \ $(OBJDIR)/json_timeline.o \ $(OBJDIR)/json_user.o \ $(OBJDIR)/json_wiki.o \ $(OBJDIR)/leaf.o \ $(OBJDIR)/loadctrl.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/path.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/publish.o \ $(OBJDIR)/purge.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ $(OBJDIR)/tar.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ $(OBJDIR)/unicode.o \ $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o APPNAME = fossil$(E) all: $(OBJDIR) $(APPNAME) install: $(APPNAME) mkdir -p $(INSTALLDIR) mv $(APPNAME) $(INSTALLDIR) codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 $(OBJDIR)/codecheck1 $(TRANS_SRC) $(OBJDIR): -mkdir $(OBJDIR) $(OBJDIR)/translate: $(SRCDIR)/translate.c $(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c $(OBJDIR)/mkbuiltin: $(SRCDIR)/mkbuiltin.c $(BCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR)/mkbuiltin.c $(OBJDIR)/mkversion: $(SRCDIR)/mkversion.c $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c $(OBJDIR)/codecheck1: $(SRCDIR)/codecheck1.c $(BCC) -o $(OBJDIR)/codecheck1 $(SRCDIR)/codecheck1.c # WARNING. DANGER. Running the test suite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h # Setup the options used to compile the included SQLite library. SQLITE_OPTIONS = -DNDEBUG=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DSQLITE_ENABLE_LOCKING_STYLE=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_FILE_FORMAT=4 \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_FTS3_PARENTHESIS # Setup the options used to compile the included SQLite shell. SHELL_OPTIONS = -Dmain=sqlite3_shell \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ -DSQLITE_SHELL_DBNAME_PROC=fossil_open # Setup the options used to compile the included miniz library. MINIZ_OPTIONS = -DMINIZ_NO_STDIO \ -DMINIZ_NO_TIME \ -DMINIZ_NO_ARCHIVE_APIS # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set # to 1. If it is set to 1, then there is no need to build or link # the sqlite3.o object. Instead, the system SQLite will be linked # using -lsqlite3. SQLITE3_OBJ.1 = SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ. = $(SQLITE3_OBJ.0) # The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or # set to 1. If it is set to 1, the miniz library included in the # source tree should be used; otherwise, it should not. MINIZ_OBJ.0 = MINIZ_OBJ.1 = $(OBJDIR)/miniz.o MINIZ_OBJ. = $(MINIZ_OBJ.0) EXTRAOBJ = \ $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \ $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ $(OBJDIR)/th_lang.o \ $(OBJDIR)/th_tcl.o \ $(OBJDIR)/cson_amalgamation.o $(APPNAME): $(OBJDIR)/headers $(OBJDIR)/codecheck1 $(OBJ) $(EXTRAOBJ) $(OBJDIR)/codecheck1 $(TRANS_SRC) $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) # This rule prevents make from using its default rules to try build # an executable named "manifest" out of the file named "manifest.c" # $(SRCDIR)/../manifest: # noop clean: rm -rf $(OBJDIR)/* $(APPNAME) $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex $(OBJDIR)/mkindex $(TRANS_SRC) >$@ $(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES) $(OBJDIR)/mkbuiltin --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \ $(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \ $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \ $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \ $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \ $(OBJDIR)/import_.c:$(OBJDIR)/import.h \ $(OBJDIR)/info_.c:$(OBJDIR)/info.h \ $(OBJDIR)/json_.c:$(OBJDIR)/json.h \ $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h \ $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h \ $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h \ $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h \ $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h \ $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h \ $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h \ $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h \ $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h \ $(OBJDIR)/json_status_.c:$(OBJDIR)/json_status.h \ $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h \ $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h \ $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h \ $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h \ $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h \ $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ $(OBJDIR)/publish_.c:$(OBJDIR)/publish.h \ $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \ $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ $(SRCDIR)/sqlite3.h \ $(SRCDIR)/th.h \ $(OBJDIR)/VERSION.h touch $(OBJDIR)/headers $(OBJDIR)/headers: Makefile $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/json_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h Makefile: $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/add.c >$@ $(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c $(OBJDIR)/add.h: $(OBJDIR)/headers $(OBJDIR)/allrepo_.c: $(SRCDIR)/allrepo.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/allrepo.c >$@ $(OBJDIR)/allrepo.o: $(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/allrepo.o -c $(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h: $(OBJDIR)/headers $(OBJDIR)/attach_.c: $(SRCDIR)/attach.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/attach.c >$@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers $(OBJDIR)/bag_.c: $(SRCDIR)/bag.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/bag.c >$@ $(OBJDIR)/bag.o: $(OBJDIR)/bag_.c $(OBJDIR)/bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c $(OBJDIR)/bag.h: $(OBJDIR)/headers $(OBJDIR)/bisect_.c: $(SRCDIR)/bisect.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/bisect.c >$@ $(OBJDIR)/bisect.o: $(OBJDIR)/bisect_.c $(OBJDIR)/bisect.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bisect.o -c $(OBJDIR)/bisect_.c $(OBJDIR)/bisect.h: $(OBJDIR)/headers $(OBJDIR)/blob_.c: $(SRCDIR)/blob.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/blob.c >$@ $(OBJDIR)/blob.o: $(OBJDIR)/blob_.c $(OBJDIR)/blob.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/blob.o -c $(OBJDIR)/blob_.c $(OBJDIR)/blob.h: $(OBJDIR)/headers $(OBJDIR)/branch_.c: $(SRCDIR)/branch.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/branch.c >$@ $(OBJDIR)/branch.o: $(OBJDIR)/branch_.c $(OBJDIR)/branch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/branch.o -c $(OBJDIR)/branch_.c $(OBJDIR)/branch.h: $(OBJDIR)/headers $(OBJDIR)/browse_.c: $(SRCDIR)/browse.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/browse.c >$@ $(OBJDIR)/browse.o: $(OBJDIR)/browse_.c $(OBJDIR)/browse.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/browse.o -c $(OBJDIR)/browse_.c $(OBJDIR)/browse.h: $(OBJDIR)/headers $(OBJDIR)/builtin_.c: $(SRCDIR)/builtin.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/builtin.c >$@ $(OBJDIR)/builtin.o: $(OBJDIR)/builtin_.c $(OBJDIR)/builtin.h $(OBJDIR)/builtin_data.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/builtin.o -c $(OBJDIR)/builtin_.c $(OBJDIR)/builtin.h: $(OBJDIR)/headers $(OBJDIR)/bundle_.c: $(SRCDIR)/bundle.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/bundle.c >$@ $(OBJDIR)/bundle.o: $(OBJDIR)/bundle_.c $(OBJDIR)/bundle.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bundle.o -c $(OBJDIR)/bundle_.c $(OBJDIR)/bundle.h: $(OBJDIR)/headers $(OBJDIR)/cache_.c: $(SRCDIR)/cache.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/cache.c >$@ $(OBJDIR)/cache.o: $(OBJDIR)/cache_.c $(OBJDIR)/cache.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cache.o -c $(OBJDIR)/cache_.c $(OBJDIR)/cache.h: $(OBJDIR)/headers $(OBJDIR)/captcha_.c: $(SRCDIR)/captcha.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/captcha.c >$@ $(OBJDIR)/captcha.o: $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h: $(OBJDIR)/headers $(OBJDIR)/cgi_.c: $(SRCDIR)/cgi.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/cgi.c >$@ $(OBJDIR)/cgi.o: $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h: $(OBJDIR)/headers $(OBJDIR)/checkin_.c: $(SRCDIR)/checkin.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/checkin.c >$@ $(OBJDIR)/checkin.o: $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h: $(OBJDIR)/headers $(OBJDIR)/checkout_.c: $(SRCDIR)/checkout.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/checkout.c >$@ $(OBJDIR)/checkout.o: $(OBJDIR)/checkout_.c $(OBJDIR)/checkout.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/checkout.o -c $(OBJDIR)/checkout_.c $(OBJDIR)/checkout.h: $(OBJDIR)/headers $(OBJDIR)/clearsign_.c: $(SRCDIR)/clearsign.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/clearsign.c >$@ $(OBJDIR)/clearsign.o: $(OBJDIR)/clearsign_.c $(OBJDIR)/clearsign.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/clearsign.o -c $(OBJDIR)/clearsign_.c $(OBJDIR)/clearsign.h: $(OBJDIR)/headers $(OBJDIR)/clone_.c: $(SRCDIR)/clone.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/clone.c >$@ $(OBJDIR)/clone.o: $(OBJDIR)/clone_.c $(OBJDIR)/clone.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c $(OBJDIR)/clone.h: $(OBJDIR)/headers $(OBJDIR)/comformat_.c: $(SRCDIR)/comformat.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/comformat.c >$@ $(OBJDIR)/comformat.o: $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/comformat.o -c $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h: $(OBJDIR)/headers $(OBJDIR)/configure_.c: $(SRCDIR)/configure.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/configure.c >$@ $(OBJDIR)/configure.o: $(OBJDIR)/configure_.c $(OBJDIR)/configure.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/configure.o -c $(OBJDIR)/configure_.c $(OBJDIR)/configure.h: $(OBJDIR)/headers $(OBJDIR)/content_.c: $(SRCDIR)/content.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/content.c >$@ $(OBJDIR)/content.o: $(OBJDIR)/content_.c $(OBJDIR)/content.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/content.o -c $(OBJDIR)/content_.c $(OBJDIR)/content.h: $(OBJDIR)/headers $(OBJDIR)/db_.c: $(SRCDIR)/db.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/db.c >$@ $(OBJDIR)/db.o: $(OBJDIR)/db_.c $(OBJDIR)/db.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/db.o -c $(OBJDIR)/db_.c $(OBJDIR)/db.h: $(OBJDIR)/headers $(OBJDIR)/delta_.c: $(SRCDIR)/delta.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/delta.c >$@ $(OBJDIR)/delta.o: $(OBJDIR)/delta_.c $(OBJDIR)/delta.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/delta.o -c $(OBJDIR)/delta_.c $(OBJDIR)/delta.h: $(OBJDIR)/headers $(OBJDIR)/deltacmd_.c: $(SRCDIR)/deltacmd.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/deltacmd.c >$@ $(OBJDIR)/deltacmd.o: $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h: $(OBJDIR)/headers $(OBJDIR)/descendants_.c: $(SRCDIR)/descendants.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/descendants.c >$@ $(OBJDIR)/descendants.o: $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h: $(OBJDIR)/headers $(OBJDIR)/diff_.c: $(SRCDIR)/diff.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/diff.c >$@ $(OBJDIR)/diff.o: $(OBJDIR)/diff_.c $(OBJDIR)/diff.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/diff.o -c $(OBJDIR)/diff_.c $(OBJDIR)/diff.h: $(OBJDIR)/headers $(OBJDIR)/diffcmd_.c: $(SRCDIR)/diffcmd.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/diffcmd.c >$@ $(OBJDIR)/diffcmd.o: $(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/diffcmd.o -c $(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h: $(OBJDIR)/headers $(OBJDIR)/doc_.c: $(SRCDIR)/doc.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/doc.c >$@ $(OBJDIR)/doc.o: $(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c $(OBJDIR)/doc.h: $(OBJDIR)/headers $(OBJDIR)/encode_.c: $(SRCDIR)/encode.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/encode.c >$@ $(OBJDIR)/encode.o: $(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c $(OBJDIR)/encode.h: $(OBJDIR)/headers $(OBJDIR)/event_.c: $(SRCDIR)/event.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/event.c >$@ $(OBJDIR)/event.o: $(OBJDIR)/event_.c $(OBJDIR)/event.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/event.o -c $(OBJDIR)/event_.c $(OBJDIR)/event.h: $(OBJDIR)/headers $(OBJDIR)/export_.c: $(SRCDIR)/export.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/export.c >$@ $(OBJDIR)/export.o: $(OBJDIR)/export_.c $(OBJDIR)/export.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/export.o -c $(OBJDIR)/export_.c $(OBJDIR)/export.h: $(OBJDIR)/headers $(OBJDIR)/file_.c: $(SRCDIR)/file.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/file.c >$@ $(OBJDIR)/file.o: $(OBJDIR)/file_.c $(OBJDIR)/file.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c $(OBJDIR)/file.h: $(OBJDIR)/headers $(OBJDIR)/finfo_.c: $(SRCDIR)/finfo.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/finfo.c >$@ $(OBJDIR)/finfo.o: $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/finfo.o -c $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h: $(OBJDIR)/headers $(OBJDIR)/foci_.c: $(SRCDIR)/foci.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/foci.c >$@ $(OBJDIR)/foci.o: $(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c $(OBJDIR)/foci.h: $(OBJDIR)/headers $(OBJDIR)/fusefs_.c: $(SRCDIR)/fusefs.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/fusefs.c >$@ $(OBJDIR)/fusefs.o: $(OBJDIR)/fusefs_.c $(OBJDIR)/fusefs.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fusefs.o -c $(OBJDIR)/fusefs_.c $(OBJDIR)/fusefs.h: $(OBJDIR)/headers $(OBJDIR)/glob_.c: $(SRCDIR)/glob.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/glob.c >$@ $(OBJDIR)/glob.o: $(OBJDIR)/glob_.c $(OBJDIR)/glob.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/glob.o -c $(OBJDIR)/glob_.c $(OBJDIR)/glob.h: $(OBJDIR)/headers $(OBJDIR)/graph_.c: $(SRCDIR)/graph.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/graph.c >$@ $(OBJDIR)/graph.o: $(OBJDIR)/graph_.c $(OBJDIR)/graph.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/graph.o -c $(OBJDIR)/graph_.c $(OBJDIR)/graph.h: $(OBJDIR)/headers $(OBJDIR)/gzip_.c: $(SRCDIR)/gzip.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/gzip.c >$@ $(OBJDIR)/gzip.o: $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h: $(OBJDIR)/headers $(OBJDIR)/http_.c: $(SRCDIR)/http.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/http.c >$@ $(OBJDIR)/http.o: $(OBJDIR)/http_.c $(OBJDIR)/http.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c $(OBJDIR)/http.h: $(OBJDIR)/headers $(OBJDIR)/http_socket_.c: $(SRCDIR)/http_socket.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/http_socket.c >$@ $(OBJDIR)/http_socket.o: $(OBJDIR)/http_socket_.c $(OBJDIR)/http_socket.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_socket.o -c $(OBJDIR)/http_socket_.c $(OBJDIR)/http_socket.h: $(OBJDIR)/headers $(OBJDIR)/http_ssl_.c: $(SRCDIR)/http_ssl.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/http_ssl.c >$@ $(OBJDIR)/http_ssl.o: $(OBJDIR)/http_ssl_.c $(OBJDIR)/http_ssl.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_ssl.o -c $(OBJDIR)/http_ssl_.c $(OBJDIR)/http_ssl.h: $(OBJDIR)/headers $(OBJDIR)/http_transport_.c: $(SRCDIR)/http_transport.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/http_transport.c >$@ $(OBJDIR)/http_transport.o: $(OBJDIR)/http_transport_.c $(OBJDIR)/http_transport.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_transport.o -c $(OBJDIR)/http_transport_.c $(OBJDIR)/http_transport.h: $(OBJDIR)/headers $(OBJDIR)/import_.c: $(SRCDIR)/import.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/import.c >$@ $(OBJDIR)/import.o: $(OBJDIR)/import_.c $(OBJDIR)/import.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/import.o -c $(OBJDIR)/import_.c $(OBJDIR)/import.h: $(OBJDIR)/headers $(OBJDIR)/info_.c: $(SRCDIR)/info.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/info.c >$@ $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c $(OBJDIR)/info.h: $(OBJDIR)/headers $(OBJDIR)/json_.c: $(SRCDIR)/json.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json.c >$@ $(OBJDIR)/json.o: $(OBJDIR)/json_.c $(OBJDIR)/json.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c $(OBJDIR)/json.h: $(OBJDIR)/headers $(OBJDIR)/json_artifact_.c: $(SRCDIR)/json_artifact.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_artifact.c >$@ $(OBJDIR)/json_artifact.o: $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h: $(OBJDIR)/headers $(OBJDIR)/json_branch_.c: $(SRCDIR)/json_branch.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_branch.c >$@ $(OBJDIR)/json_branch.o: $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h: $(OBJDIR)/headers $(OBJDIR)/json_config_.c: $(SRCDIR)/json_config.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_config.c >$@ $(OBJDIR)/json_config.o: $(OBJDIR)/json_config_.c $(OBJDIR)/json_config.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_config.o -c $(OBJDIR)/json_config_.c $(OBJDIR)/json_config.h: $(OBJDIR)/headers $(OBJDIR)/json_diff_.c: $(SRCDIR)/json_diff.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_diff.c >$@ $(OBJDIR)/json_diff.o: $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h: $(OBJDIR)/headers $(OBJDIR)/json_dir_.c: $(SRCDIR)/json_dir.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_dir.c >$@ $(OBJDIR)/json_dir.o: $(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_dir.o -c $(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h: $(OBJDIR)/headers $(OBJDIR)/json_finfo_.c: $(SRCDIR)/json_finfo.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_finfo.c >$@ $(OBJDIR)/json_finfo.o: $(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_finfo.o -c $(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h: $(OBJDIR)/headers $(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_login.c >$@ $(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h: $(OBJDIR)/headers $(OBJDIR)/json_query_.c: $(SRCDIR)/json_query.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_query.c >$@ $(OBJDIR)/json_query.o: $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h: $(OBJDIR)/headers $(OBJDIR)/json_report_.c: $(SRCDIR)/json_report.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_report.c >$@ $(OBJDIR)/json_report.o: $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h: $(OBJDIR)/headers $(OBJDIR)/json_status_.c: $(SRCDIR)/json_status.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_status.c >$@ $(OBJDIR)/json_status.o: $(OBJDIR)/json_status_.c $(OBJDIR)/json_status.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_status.o -c $(OBJDIR)/json_status_.c $(OBJDIR)/json_status.h: $(OBJDIR)/headers $(OBJDIR)/json_tag_.c: $(SRCDIR)/json_tag.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_tag.c >$@ $(OBJDIR)/json_tag.o: $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h: $(OBJDIR)/headers $(OBJDIR)/json_timeline_.c: $(SRCDIR)/json_timeline.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_timeline.c >$@ $(OBJDIR)/json_timeline.o: $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h: $(OBJDIR)/headers $(OBJDIR)/json_user_.c: $(SRCDIR)/json_user.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_user.c >$@ $(OBJDIR)/json_user.o: $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h: $(OBJDIR)/headers $(OBJDIR)/json_wiki_.c: $(SRCDIR)/json_wiki.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/json_wiki.c >$@ $(OBJDIR)/json_wiki.o: $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h: $(OBJDIR)/headers $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/leaf.c >$@ $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h: $(OBJDIR)/headers $(OBJDIR)/loadctrl_.c: $(SRCDIR)/loadctrl.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/loadctrl.c >$@ $(OBJDIR)/loadctrl.o: $(OBJDIR)/loadctrl_.c $(OBJDIR)/loadctrl.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/loadctrl.o -c $(OBJDIR)/loadctrl_.c $(OBJDIR)/loadctrl.h: $(OBJDIR)/headers $(OBJDIR)/login_.c: $(SRCDIR)/login.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/login.c >$@ $(OBJDIR)/login.o: $(OBJDIR)/login_.c $(OBJDIR)/login.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/login.o -c $(OBJDIR)/login_.c $(OBJDIR)/login.h: $(OBJDIR)/headers $(OBJDIR)/lookslike_.c: $(SRCDIR)/lookslike.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/lookslike.c >$@ $(OBJDIR)/lookslike.o: $(OBJDIR)/lookslike_.c $(OBJDIR)/lookslike.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/lookslike.o -c $(OBJDIR)/lookslike_.c $(OBJDIR)/lookslike.h: $(OBJDIR)/headers $(OBJDIR)/main_.c: $(SRCDIR)/main.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/main.c >$@ $(OBJDIR)/main.o: $(OBJDIR)/main_.c $(OBJDIR)/main.h $(OBJDIR)/page_index.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/main.o -c $(OBJDIR)/main_.c $(OBJDIR)/main.h: $(OBJDIR)/headers $(OBJDIR)/manifest_.c: $(SRCDIR)/manifest.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/manifest.c >$@ $(OBJDIR)/manifest.o: $(OBJDIR)/manifest_.c $(OBJDIR)/manifest.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/manifest.o -c $(OBJDIR)/manifest_.c $(OBJDIR)/manifest.h: $(OBJDIR)/headers $(OBJDIR)/markdown_.c: $(SRCDIR)/markdown.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/markdown.c >$@ $(OBJDIR)/markdown.o: $(OBJDIR)/markdown_.c $(OBJDIR)/markdown.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown.o -c $(OBJDIR)/markdown_.c $(OBJDIR)/markdown.h: $(OBJDIR)/headers $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c $(OBJDIR)/md5.h: $(OBJDIR)/headers $(OBJDIR)/merge_.c: $(SRCDIR)/merge.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/merge.c >$@ $(OBJDIR)/merge.o: $(OBJDIR)/merge_.c $(OBJDIR)/merge.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/merge.o -c $(OBJDIR)/merge_.c $(OBJDIR)/merge.h: $(OBJDIR)/headers $(OBJDIR)/merge3_.c: $(SRCDIR)/merge3.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/merge3.c >$@ $(OBJDIR)/merge3.o: $(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h: $(OBJDIR)/headers $(OBJDIR)/moderate_.c: $(SRCDIR)/moderate.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/moderate.c >$@ $(OBJDIR)/moderate.o: $(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h: $(OBJDIR)/headers $(OBJDIR)/name_.c: $(SRCDIR)/name.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/name.c >$@ $(OBJDIR)/name.o: $(OBJDIR)/name_.c $(OBJDIR)/name.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c $(OBJDIR)/name.h: $(OBJDIR)/headers $(OBJDIR)/path_.c: $(SRCDIR)/path.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/path.c >$@ $(OBJDIR)/path.o: $(OBJDIR)/path_.c $(OBJDIR)/path.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/path.o -c $(OBJDIR)/path_.c $(OBJDIR)/path.h: $(OBJDIR)/headers $(OBJDIR)/pivot_.c: $(SRCDIR)/pivot.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/pivot.c >$@ $(OBJDIR)/pivot.o: $(OBJDIR)/pivot_.c $(OBJDIR)/pivot.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pivot.o -c $(OBJDIR)/pivot_.c $(OBJDIR)/pivot.h: $(OBJDIR)/headers $(OBJDIR)/popen_.c: $(SRCDIR)/popen.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/popen.c >$@ $(OBJDIR)/popen.o: $(OBJDIR)/popen_.c $(OBJDIR)/popen.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/popen.o -c $(OBJDIR)/popen_.c $(OBJDIR)/popen.h: $(OBJDIR)/headers $(OBJDIR)/pqueue_.c: $(SRCDIR)/pqueue.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/pqueue.c >$@ $(OBJDIR)/pqueue.o: $(OBJDIR)/pqueue_.c $(OBJDIR)/pqueue.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pqueue.o -c $(OBJDIR)/pqueue_.c $(OBJDIR)/pqueue.h: $(OBJDIR)/headers $(OBJDIR)/printf_.c: $(SRCDIR)/printf.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/printf.c >$@ $(OBJDIR)/printf.o: $(OBJDIR)/printf_.c $(OBJDIR)/printf.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/printf.o -c $(OBJDIR)/printf_.c $(OBJDIR)/printf.h: $(OBJDIR)/headers $(OBJDIR)/publish_.c: $(SRCDIR)/publish.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/publish.c >$@ $(OBJDIR)/publish.o: $(OBJDIR)/publish_.c $(OBJDIR)/publish.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/publish.o -c $(OBJDIR)/publish_.c $(OBJDIR)/publish.h: $(OBJDIR)/headers $(OBJDIR)/purge_.c: $(SRCDIR)/purge.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/purge.c >$@ $(OBJDIR)/purge.o: $(OBJDIR)/purge_.c $(OBJDIR)/purge.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/purge.o -c $(OBJDIR)/purge_.c $(OBJDIR)/purge.h: $(OBJDIR)/headers $(OBJDIR)/rebuild_.c: $(SRCDIR)/rebuild.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/rebuild.c >$@ $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h: $(OBJDIR)/headers $(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/regexp.c >$@ $(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h: $(OBJDIR)/headers $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/report.c >$@ $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c $(OBJDIR)/report.h: $(OBJDIR)/headers $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/rss.c >$@ $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c $(OBJDIR)/rss.h: $(OBJDIR)/headers $(OBJDIR)/schema_.c: $(SRCDIR)/schema.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/schema.c >$@ $(OBJDIR)/schema.o: $(OBJDIR)/schema_.c $(OBJDIR)/schema.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/schema.o -c $(OBJDIR)/schema_.c $(OBJDIR)/schema.h: $(OBJDIR)/headers $(OBJDIR)/search_.c: $(SRCDIR)/search.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/search.c >$@ $(OBJDIR)/search.o: $(OBJDIR)/search_.c $(OBJDIR)/search.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/search.o -c $(OBJDIR)/search_.c $(OBJDIR)/search.h: $(OBJDIR)/headers $(OBJDIR)/setup_.c: $(SRCDIR)/setup.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/setup.c >$@ $(OBJDIR)/setup.o: $(OBJDIR)/setup_.c $(OBJDIR)/setup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c $(OBJDIR)/setup.h: $(OBJDIR)/headers $(OBJDIR)/sha1_.c: $(SRCDIR)/sha1.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sha1.c >$@ $(OBJDIR)/sha1.o: $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h: $(OBJDIR)/headers $(OBJDIR)/shun_.c: $(SRCDIR)/shun.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/shun.c >$@ $(OBJDIR)/shun.o: $(OBJDIR)/shun_.c $(OBJDIR)/shun.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/shun.o -c $(OBJDIR)/shun_.c $(OBJDIR)/shun.h: $(OBJDIR)/headers $(OBJDIR)/sitemap_.c: $(SRCDIR)/sitemap.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sitemap.c >$@ $(OBJDIR)/sitemap.o: $(OBJDIR)/sitemap_.c $(OBJDIR)/sitemap.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sitemap.o -c $(OBJDIR)/sitemap_.c $(OBJDIR)/sitemap.h: $(OBJDIR)/headers $(OBJDIR)/skins_.c: $(SRCDIR)/skins.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/skins.c >$@ $(OBJDIR)/skins.o: $(OBJDIR)/skins_.c $(OBJDIR)/skins.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c $(OBJDIR)/skins.h: $(OBJDIR)/headers $(OBJDIR)/sqlcmd_.c: $(SRCDIR)/sqlcmd.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sqlcmd.c >$@ $(OBJDIR)/sqlcmd.o: $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h: $(OBJDIR)/headers $(OBJDIR)/stash_.c: $(SRCDIR)/stash.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/stash.c >$@ $(OBJDIR)/stash.o: $(OBJDIR)/stash_.c $(OBJDIR)/stash.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/stash.o -c $(OBJDIR)/stash_.c $(OBJDIR)/stash.h: $(OBJDIR)/headers $(OBJDIR)/stat_.c: $(SRCDIR)/stat.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/stat.c >$@ $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c $(OBJDIR)/stat.h: $(OBJDIR)/headers $(OBJDIR)/statrep_.c: $(SRCDIR)/statrep.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/statrep.c >$@ $(OBJDIR)/statrep.o: $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/statrep.o -c $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h: $(OBJDIR)/headers $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/style.c >$@ $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/style.o -c $(OBJDIR)/style_.c $(OBJDIR)/style.h: $(OBJDIR)/headers $(OBJDIR)/sync_.c: $(SRCDIR)/sync.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/sync.c >$@ $(OBJDIR)/sync.o: $(OBJDIR)/sync_.c $(OBJDIR)/sync.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sync.o -c $(OBJDIR)/sync_.c $(OBJDIR)/sync.h: $(OBJDIR)/headers $(OBJDIR)/tag_.c: $(SRCDIR)/tag.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/tag.c >$@ $(OBJDIR)/tag.o: $(OBJDIR)/tag_.c $(OBJDIR)/tag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tag.o -c $(OBJDIR)/tag_.c $(OBJDIR)/tag.h: $(OBJDIR)/headers $(OBJDIR)/tar_.c: $(SRCDIR)/tar.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/tar.c >$@ $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c $(OBJDIR)/tar.h: $(OBJDIR)/headers $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/th_main.c >$@ $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/th_main.o -c $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h: $(OBJDIR)/headers $(OBJDIR)/timeline_.c: $(SRCDIR)/timeline.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/timeline.c >$@ $(OBJDIR)/timeline.o: $(OBJDIR)/timeline_.c $(OBJDIR)/timeline.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/timeline.o -c $(OBJDIR)/timeline_.c $(OBJDIR)/timeline.h: $(OBJDIR)/headers $(OBJDIR)/tkt_.c: $(SRCDIR)/tkt.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/tkt.c >$@ $(OBJDIR)/tkt.o: $(OBJDIR)/tkt_.c $(OBJDIR)/tkt.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tkt.o -c $(OBJDIR)/tkt_.c $(OBJDIR)/tkt.h: $(OBJDIR)/headers $(OBJDIR)/tktsetup_.c: $(SRCDIR)/tktsetup.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/tktsetup.c >$@ $(OBJDIR)/tktsetup.o: $(OBJDIR)/tktsetup_.c $(OBJDIR)/tktsetup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tktsetup.o -c $(OBJDIR)/tktsetup_.c $(OBJDIR)/tktsetup.h: $(OBJDIR)/headers $(OBJDIR)/undo_.c: $(SRCDIR)/undo.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/undo.c >$@ $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c $(OBJDIR)/undo.h: $(OBJDIR)/headers $(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/unicode.c >$@ $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h: $(OBJDIR)/headers $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/update.c >$@ $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c $(OBJDIR)/update.h: $(OBJDIR)/headers $(OBJDIR)/url_.c: $(SRCDIR)/url.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/url.c >$@ $(OBJDIR)/url.o: $(OBJDIR)/url_.c $(OBJDIR)/url.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/url.o -c $(OBJDIR)/url_.c $(OBJDIR)/url.h: $(OBJDIR)/headers $(OBJDIR)/user_.c: $(SRCDIR)/user.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/user.c >$@ $(OBJDIR)/user.o: $(OBJDIR)/user_.c $(OBJDIR)/user.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/user.o -c $(OBJDIR)/user_.c $(OBJDIR)/user.h: $(OBJDIR)/headers $(OBJDIR)/utf8_.c: $(SRCDIR)/utf8.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/utf8.c >$@ $(OBJDIR)/utf8.o: $(OBJDIR)/utf8_.c $(OBJDIR)/utf8.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/utf8.o -c $(OBJDIR)/utf8_.c $(OBJDIR)/utf8.h: $(OBJDIR)/headers $(OBJDIR)/util_.c: $(SRCDIR)/util.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/util.c >$@ $(OBJDIR)/util.o: $(OBJDIR)/util_.c $(OBJDIR)/util.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/util.o -c $(OBJDIR)/util_.c $(OBJDIR)/util.h: $(OBJDIR)/headers $(OBJDIR)/verify_.c: $(SRCDIR)/verify.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/verify.c >$@ $(OBJDIR)/verify.o: $(OBJDIR)/verify_.c $(OBJDIR)/verify.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/verify.o -c $(OBJDIR)/verify_.c $(OBJDIR)/verify.h: $(OBJDIR)/headers $(OBJDIR)/vfile_.c: $(SRCDIR)/vfile.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/vfile.c >$@ $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h: $(OBJDIR)/headers $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/wiki.c >$@ $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h: $(OBJDIR)/headers $(OBJDIR)/wikiformat_.c: $(SRCDIR)/wikiformat.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/wikiformat.c >$@ $(OBJDIR)/wikiformat.o: $(OBJDIR)/wikiformat_.c $(OBJDIR)/wikiformat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wikiformat.o -c $(OBJDIR)/wikiformat_.c $(OBJDIR)/wikiformat.h: $(OBJDIR)/headers $(OBJDIR)/winfile_.c: $(SRCDIR)/winfile.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/winfile.c >$@ $(OBJDIR)/winfile.o: $(OBJDIR)/winfile_.c $(OBJDIR)/winfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/winfile.o -c $(OBJDIR)/winfile_.c $(OBJDIR)/winfile.h: $(OBJDIR)/headers $(OBJDIR)/winhttp_.c: $(SRCDIR)/winhttp.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/winhttp.c >$@ $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h: $(OBJDIR)/headers $(OBJDIR)/wysiwyg_.c: $(SRCDIR)/wysiwyg.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/wysiwyg.c >$@ $(OBJDIR)/wysiwyg.o: $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h: $(OBJDIR)/headers $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/xfer.c >$@ $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h: $(OBJDIR)/headers $(OBJDIR)/xfersetup_.c: $(SRCDIR)/xfersetup.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/xfersetup.c >$@ $(OBJDIR)/xfersetup.o: $(OBJDIR)/xfersetup_.c $(OBJDIR)/xfersetup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/xfersetup.o -c $(OBJDIR)/xfersetup_.c $(OBJDIR)/xfersetup.h: $(OBJDIR)/headers $(OBJDIR)/zip_.c: $(SRCDIR)/zip.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/zip.c >$@ $(OBJDIR)/zip.o: $(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c $(OBJDIR)/zip.h: $(OBJDIR)/headers $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) -c $(SRCDIR)/sqlite3.c -o $@ $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)/shell.c -o $@ $(OBJDIR)/th.o: $(SRCDIR)/th.c $(XTCC) -c $(SRCDIR)/th.c -o $@ $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c $(XTCC) -c $(SRCDIR)/th_lang.c -o $@ $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c $(XTCC) -c $(SRCDIR)/th_tcl.c -o $@ $(OBJDIR)/miniz.o: $(SRCDIR)/miniz.c $(XTCC) $(MINIZ_OPTIONS) -c $(SRCDIR)/miniz.c -o $@ $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $@ # # The list of all the targets that do not correspond to real files. This stops # 'make' from getting confused when someone makes an error in a rule. # .PHONY: all install test clean |
Changes to src/makeheaders.c.
︙ | ︙ | |||
35 36 37 38 39 40 41 42 43 44 45 | */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <memory.h> #include <sys/stat.h> #include <assert.h> #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) # ifndef WIN32 # define WIN32 # endif | > > < | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <memory.h> #include <sys/stat.h> #include <assert.h> #include <string.h> #if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__) # ifndef WIN32 # define WIN32 # endif #else # include <unistd.h> #endif /* ** Macros for debugging. */ |
︙ | ︙ | |||
109 110 111 112 113 114 115 | ** doesn't tell about the objects structure. A typical forward declaration ** is: ** ** struct Xyzzy; ** ** Not every object has a forward declaration. If it does, thought, the ** forward declaration will be contained in the zFwd field for C and | | | | | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | ** doesn't tell about the objects structure. A typical forward declaration ** is: ** ** struct Xyzzy; ** ** Not every object has a forward declaration. If it does, thought, the ** forward declaration will be contained in the zFwd field for C and ** the zFwdCpp for C++. The zDecl field contains the complete ** declaration text. */ typedef struct Decl Decl; struct Decl { char *zName; /* Name of the object being declared. The appearance ** of this name is a source file triggers the declaration ** to be added to the header for that file. */ const char *zFile; /* File from which extracted. */ char *zIf; /* Surround the declaration with this #if */ char *zFwd; /* A forward declaration. NULL if there is none. */ char *zFwdCpp; /* Use this forward declaration for C++. */ char *zDecl; /* A full declaration of this object */ char *zExtra; /* Extra declaration text inserted into class objects */ int extraType; /* Last public:, protected: or private: in zExtraDecl */ struct Include *pInclude; /* #includes that come before this declaration */ |
︙ | ︙ | |||
160 161 162 163 164 165 166 | ** a library, then the object is not visible to users ** of the library. (i.e. the object does not appear ** in the output when using the -H option.) ** ** EXPORT scope The object is visible and usable everywhere. ** ** The DP_Flag is a temporary use flag that is used during processing to | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | ** a library, then the object is not visible to users ** of the library. (i.e. the object does not appear ** in the output when using the -H option.) ** ** EXPORT scope The object is visible and usable everywhere. ** ** The DP_Flag is a temporary use flag that is used during processing to ** prevent an infinite loop. It's use is localized. ** ** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent ** and are used to specify what type of declaration the object requires. */ #define DP_Forward 0x001 /* Has a forward declaration in this file */ #define DP_Declared 0x002 /* Has a full declaration in this file */ #define DP_Export 0x004 /* Export this declaration */ |
︙ | ︙ | |||
198 199 200 201 202 203 204 | ** the same "flags" field. ** ** Be careful not to confuse PS_Export with DP_Export or ** PS_Local with DP_Local. Their names are similar, but the meanings ** of these flags are very different. */ #define PS_Extern 0x000800 /* "extern" has been seen */ | | | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | ** the same "flags" field. ** ** Be careful not to confuse PS_Export with DP_Export or ** PS_Local with DP_Local. Their names are similar, but the meanings ** of these flags are very different. */ #define PS_Extern 0x000800 /* "extern" has been seen */ #define PS_Export 0x001000 /* If between "#if EXPORT_INTERFACE" ** and "#endif" */ #define PS_Export2 0x002000 /* If "EXPORT" seen */ #define PS_Typedef 0x004000 /* If "typedef" has been seen */ #define PS_Static 0x008000 /* If "static" has been seen */ #define PS_Interface 0x010000 /* If within #if INTERFACE..#endif */ #define PS_Method 0x020000 /* If "::" token has been seen */ #define PS_Local 0x040000 /* If within #if LOCAL_INTERFACE..#endif */ |
︙ | ︙ | |||
228 229 230 231 232 233 234 | #define TY_Variable 0x01000000 #define TY_Structure 0x02000000 #define TY_Union 0x04000000 #define TY_Enumeration 0x08000000 #define TY_Defunct 0x10000000 /* Used to erase a declaration */ /* | | | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | #define TY_Variable 0x01000000 #define TY_Structure 0x02000000 #define TY_Union 0x04000000 #define TY_Enumeration 0x08000000 #define TY_Defunct 0x10000000 /* Used to erase a declaration */ /* ** Each nested #if (or #ifdef or #ifndef) is stored in a stack of ** instances of the following structure. */ typedef struct Ifmacro Ifmacro; struct Ifmacro { int nLine; /* Line number where this macro occurs */ char *zCondition; /* Text of the condition for this macro */ Ifmacro *pNext; /* Next down in the stack */ |
︙ | ︙ | |||
290 291 292 293 294 295 296 | char *zHdr; /* Name of the generated .h file for this input. ** Will be NULL if input is to be scanned only */ int flags; /* One or more DP_, PS_ and/or TY_ flags */ InFile *pNext; /* Next input file in the list of them all */ IdentTable idTable; /* All identifiers in this input file */ }; | | | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | char *zHdr; /* Name of the generated .h file for this input. ** Will be NULL if input is to be scanned only */ int flags; /* One or more DP_, PS_ and/or TY_ flags */ InFile *pNext; /* Next input file in the list of them all */ IdentTable idTable; /* All identifiers in this input file */ }; /* ** An unbounded string is able to grow without limit. We use these ** to construct large in-memory strings from lots of smaller components. */ typedef struct String String; struct String { int nAlloc; /* Number of bytes allocated */ int nUsed; /* Number of bytes used (not counting null terminator) */ |
︙ | ︙ | |||
329 330 331 332 333 334 335 | ** The following text line appears at the top of every file generated ** by this program. By recognizing this line, the program can be sure ** never to read a file that it generated itself. ** ** The "#undef INTERFACE" part is a hack to work around a name collision ** in MSVC 2008. */ | | | | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | ** The following text line appears at the top of every file generated ** by this program. By recognizing this line, the program can be sure ** never to read a file that it generated itself. ** ** The "#undef INTERFACE" part is a hack to work around a name collision ** in MSVC 2008. */ const char zTopLine[] = "/* \aThis file was automatically generated. Do not edit! */\n" "#undef INTERFACE\n"; #define nTopLine (sizeof(zTopLine)-1) /* ** The name of the file currently being parsed. */ static const char *zFilename; /* ** The stack of #if macros for the file currently being parsed. */ static Ifmacro *ifStack = 0; /* |
︙ | ︙ | |||
699 700 701 702 703 704 705 | */ static char *ReadFile(const char *zFilename){ struct stat sStat; FILE *pIn; char *zBuf; int n; | | | 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 | */ static char *ReadFile(const char *zFilename){ struct stat sStat; FILE *pIn; char *zBuf; int n; if( stat(zFilename,&sStat)!=0 #ifndef WIN32 || !S_ISREG(sStat.st_mode) #endif ){ return 0; } pIn = fopen(zFilename,"r"); |
︙ | ︙ | |||
886 887 888 889 890 891 892 | }else{ isBlockComment = 0; } } } i++; } | | | | | 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 | }else{ isBlockComment = 0; } } } i++; } if( z[i] ){ i += 2; }else{ isBlockComment = 0; fprintf(stderr,"%s:%d: Unterminated comment\n", zFilename, startLine); nErr++; } pToken->eType = isBlockComment==2 ? TT_BlockComment : TT_Comment; pToken->nText = i - pIn->i; }else{ /* A divide operator */ pToken->eType = TT_Other; pToken->nText = 1 + (z[i+1]=='+'); } break; case '0': if( z[i+1]=='x' || z[i+1]=='X' ){ /* A hex constant */ i += 2; while( isxdigit(z[i]) ){ i++; } }else{ /* An octal constant */ while( isdigit(z[i]) ){ i++; } |
︙ | ︙ | |||
960 961 962 963 964 965 966 | case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_': while( isalnum(z[i]) || z[i]=='_' ){ i++; }; pToken->eType = TT_Id; pToken->nText = i - pIn->i; break; | | | | 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 | case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_': while( isalnum(z[i]) || z[i]=='_' ){ i++; }; pToken->eType = TT_Id; pToken->nText = i - pIn->i; break; case ':': pToken->eType = TT_Other; pToken->nText = 1 + (z[i+1]==':'); break; case '=': case '<': case '>': case '+': case '-': case '*': case '%': case '^': case '&': case '|': pToken->eType = TT_Other; pToken->nText = 1 + (z[i+1]=='='); break; default: pToken->eType = TT_Other; pToken->nText = 1; |
︙ | ︙ | |||
1061 1062 1063 1064 1065 1066 1067 | } break; } } /* NOT REACHED */ } | | | 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 | } break; } } /* NOT REACHED */ } /* ** This routine looks for identifiers (strings of contiguous alphanumeric ** characters) within a preprocessor directive and adds every such string ** found to the given identifier table */ static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){ Token sToken; InStream sIn; |
︙ | ︙ | |||
1154 1155 1156 1157 1158 1159 1160 | return nErr; case TT_Id: if( pTable ){ IdentTableInsert(pTable,pToken->zText,pToken->nText); } break; | | | 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 | return nErr; case TT_Id: if( pTable ){ IdentTableInsert(pTable,pToken->zText,pToken->nText); } break; case TT_Preprocessor: if( pTable!=0 ){ FindIdentifiersInMacro(pToken,pTable); } break; case TT_Other: |
︙ | ︙ | |||
1260 1261 1262 1263 1264 1265 1266 | if( zFile==0 ){ fprintf(stderr,"Can't read file \"%s\"\n",argv[1]); exit(1); } pList = TokenizeFile(zFile,&sTable); for(p=pList; p; p=p->pNext){ int j; | | | 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 | if( zFile==0 ){ fprintf(stderr,"Can't read file \"%s\"\n",argv[1]); exit(1); } pList = TokenizeFile(zFile,&sTable); for(p=pList; p; p=p->pNext){ int j; switch( p->eType ){ case TT_Space: printf("%4d: Space\n",p->nLine); break; case TT_Id: printf("%4d: Id %.*s\n",p->nLine,p->nText,p->zText); break; case TT_Preprocessor: |
︙ | ︙ | |||
1327 1328 1329 1330 1331 1332 1333 | case TT_Number: printf("%s%.*s", needSpace ? " " : "", pFirst->nText, pFirst->zText); needSpace = 1; break; default: c = pFirst->zText[0]; | | | 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 | case TT_Number: printf("%s%.*s", needSpace ? " " : "", pFirst->nText, pFirst->zText); needSpace = 1; break; default: c = pFirst->zText[0]; printf("%s%.*s", (needSpace && (c=='*' || c=='{')) ? " " : "", pFirst->nText, pFirst->zText); needSpace = pFirst->zText[0]==','; break; } pFirst = pFirst->pNext; } |
︙ | ︙ | |||
1368 1369 1370 1371 1372 1373 1374 | int iSkip = 0; int skipOne = 0; StringInit(&str); pLast = pLast->pNext; while( pFirst!=pLast ){ if( pFirst==pSkip ){ iSkip = nSkip; } | | | | | | 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 | int iSkip = 0; int skipOne = 0; StringInit(&str); pLast = pLast->pNext; while( pFirst!=pLast ){ if( pFirst==pSkip ){ iSkip = nSkip; } if( iSkip>0 ){ iSkip--; pFirst=pFirst->pNext; continue; } switch( pFirst->eType ){ case TT_Preprocessor: StringAppend(&str,"\n",1); StringAppend(&str,pFirst->zText,pFirst->nText); StringAppend(&str,"\n",1); needSpace = 0; break; case TT_Id: switch( pFirst->zText[0] ){ case 'E': if( pFirst->nText==6 && strncmp(pFirst->zText,"EXPORT",6)==0 ){ skipOne = 1; } break; case 'P': switch( pFirst->nText ){ case 6: skipOne = !strncmp(pFirst->zText,"PUBLIC", 6); break; |
︙ | ︙ | |||
1642 1643 1644 1645 1646 1647 1648 | return 0; } pLast = pLast->pNext; for(p=pFirst; p && p!=pLast; p=p->pNext){ if( p->eType==TT_Id ){ static IdentTable sReserved; static int isInit = 0; | | | | | | 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 | return 0; } pLast = pLast->pNext; for(p=pFirst; p && p!=pLast; p=p->pNext){ if( p->eType==TT_Id ){ static IdentTable sReserved; static int isInit = 0; static const char *aWords[] = { "char", "class", "const", "double", "enum", "extern", "EXPORT", "ET_PROC", "float", "int", "long", "PRIVATE", "PROTECTED", "PUBLIC", "register", "static", "struct", "sizeof", "signed", "typedef", "union", "volatile", "virtual", "void", }; if( !isInit ){ int i; for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){ IdentTableInsert(&sReserved,aWords[i],0); } isInit = 1; } |
︙ | ︙ | |||
1765 1766 1767 1768 1769 1770 1771 | return 0; } pCode = pLast; while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){ pLast = pLast->pPrev; } if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){ | | | 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 | return 0; } pCode = pLast; while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){ pLast = pLast->pPrev; } if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){ fprintf(stderr,"%s:%d: Unrecognized syntax.\n", zFilename, pFirst->nLine); return 1; } if( flags & (PS_Interface|PS_Export|PS_Local) ){ fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n", zFilename, pFirst->nLine); return 1; |
︙ | ︙ | |||
1846 1847 1848 1849 1850 1851 1852 | fprintf(stderr,"%s:%d: malformed inline procedure definition\n", zFilename, pFirst->nLine); return 1; } #ifdef DEBUG if( debugMask & PARSER ){ | | | 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 | fprintf(stderr,"%s:%d: malformed inline procedure definition\n", zFilename, pFirst->nLine); return 1; } #ifdef DEBUG if( debugMask & PARSER ){ printf("**** Found inline routine: %.*s on line %d...\n", pName->nText, pName->zText, pFirst->nLine); PrintTokens(pFirst,pEnd); printf("\n"); } #endif pDecl = CreateDecl(pName->zText,pName->nText); pDecl->pComment = pFirst->pComment; |
︙ | ︙ | |||
1885 1886 1887 1888 1889 1890 1891 | ** ** If pEnd is ';', then the determination is more difficult. We have ** to search for an occurrence of an ID followed immediately by '('. ** If found, we have a prototype. Otherwise we are dealing with a ** variable definition. */ static int isVariableDef(Token *pFirst, Token *pEnd){ | | | 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 | ** ** If pEnd is ';', then the determination is more difficult. We have ** to search for an occurrence of an ID followed immediately by '('. ** If found, we have a prototype. Otherwise we are dealing with a ** variable definition. */ static int isVariableDef(Token *pFirst, Token *pEnd){ if( pEnd && pEnd->zText[0]=='=' && (pEnd->pPrev->nText!=8 || strncmp(pEnd->pPrev->zText,"operator",8)!=0) ){ return 1; } while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){ if( pFirst->eType==TT_Id && pFirst->pNext->zText[0]=='(' ){ return 0; |
︙ | ︙ | |||
1946 1947 1948 1949 1950 1951 1952 | ** option was specified or the "LOCAL" keyword is used. */ return nErr; } while( pFirst!=0 && pFirst->pNext!=pEnd && ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0) || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0)) ){ | | | | 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 | ** option was specified or the "LOCAL" keyword is used. */ return nErr; } while( pFirst!=0 && pFirst->pNext!=pEnd && ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0) || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0)) ){ /* Lose the initial "static" or local from local variables. ** We'll prepend "extern" later. */ pFirst = pFirst->pNext; isLocal = 1; } if( pFirst==0 || !isLocal ){ return nErr; } }else if( flags & PS_Method ){ /* Methods are declared by their class. Don't declare separately. */ return nErr; } isVar = (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd); if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0 && (flags & PS_Extern)==0 ){ fprintf(stderr,"%s:%d: Can't define a variable in this context\n", zFilename, pFirst->nLine); nErr++; } pName = FindDeclName(pFirst,pEnd->pPrev); if( pName==0 ){ |
︙ | ︙ | |||
2092 2093 2094 2095 2096 2097 2098 | nCmd = 1; while( isalpha(zCmd[nCmd]) ){ nCmd++; } if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){ /* | | | | 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 | nCmd = 1; while( isalpha(zCmd[nCmd]) ){ nCmd++; } if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){ /* ** Pop the if stack */ pIf = ifStack; if( pIf==0 ){ fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine); return 1; } ifStack = pIf->pNext; SafeFree(pIf); }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){ /* ** Record a #define if we are in PS_Interface or PS_Export */ Decl *pDecl; if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; } zArg = &zCmd[6]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ zArg++; } |
︙ | ︙ | |||
2126 2127 2128 2129 2130 2131 2132 | if( flags & PS_Export ){ DeclSetProperty(pDecl,DP_Export); }else if( flags & PS_Local ){ DeclSetProperty(pDecl,DP_Local); } }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){ /* | | | 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 | if( flags & PS_Export ){ DeclSetProperty(pDecl,DP_Export); }else if( flags & PS_Local ){ DeclSetProperty(pDecl,DP_Local); } }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){ /* ** Record an #include if we are in PS_Interface or PS_Export */ Include *pInclude; char *zIf; if( !(flags & (PS_Interface|PS_Export)) ){ return 0; } zArg = &zCmd[7]; while( *zArg && isspace(*zArg) ){ zArg++; } |
︙ | ︙ | |||
2181 2182 2183 2184 2185 2186 2187 | PushIfMacro(0,0,0,pToken->nLine,PS_Export); }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){ PushIfMacro(0,0,0,pToken->nLine,PS_Local); }else{ PushIfMacro(0,zArg,nArg,pToken->nLine,0); } }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){ | | | 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 | PushIfMacro(0,0,0,pToken->nLine,PS_Export); }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){ PushIfMacro(0,0,0,pToken->nLine,PS_Local); }else{ PushIfMacro(0,zArg,nArg,pToken->nLine,0); } }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){ /* ** Push an #ifdef. */ zArg = &zCmd[5]; while( *zArg && isspace(*zArg) && *zArg!='\n' ){ zArg++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } |
︙ | ︙ | |||
2204 2205 2206 2207 2208 2209 2210 | zArg++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } nArg = pToken->nText + (int)(pToken->zText - zArg); PushIfMacro("!defined",zArg,nArg,pToken->nLine,0); }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){ /* | | | | | | | | 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 | zArg++; } if( *zArg==0 || *zArg=='\n' ){ return 0; } nArg = pToken->nText + (int)(pToken->zText - zArg); PushIfMacro("!defined",zArg,nArg,pToken->nLine,0); }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){ /* ** Invert the #if on the top of the stack */ if( ifStack==0 ){ fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename, pToken->nLine); return 1; } pIf = ifStack; if( pIf->zCondition ){ ifStack = ifStack->pNext; PushIfMacro("!",pIf->zCondition,strlen(pIf->zCondition),pIf->nLine,0); SafeFree(pIf); }else{ pIf->flags = 0; } }else{ /* ** This directive can be safely ignored */ return 0; } /* ** Recompute the preset flags */ *pPresetFlags = 0; for(pIf = ifStack; pIf; pIf=pIf->pNext){ *pPresetFlags |= pIf->flags; } return nErr; } /* ** Parse an entire file. Return the number of errors. ** ** pList is a list of tokens in the file. Whitespace tokens have been ** eliminated, and text with {...} has been collapsed into a ** single TT_Brace token. ** ** initFlags are a set of parse flags that should always be set for this ** file. For .c files this is normally 0. For .h files it is PS_Interface. */ static int ParseFile(Token *pList, int initFlags){ int nErr = 0; Token *pStart = 0; int flags = initFlags; |
︙ | ︙ | |||
2276 2277 2278 2279 2280 2281 2282 | case ';': nErr += ProcessDecl(pStart,pList,flags); pStart = 0; flags = presetFlags; break; case '=': | | | 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 | case ';': nErr += ProcessDecl(pStart,pList,flags); pStart = 0; flags = presetFlags; break; case '=': if( pList->pPrev->nText==8 && strncmp(pList->pPrev->zText,"operator",8)==0 ){ break; } nErr += ProcessDecl(pStart,pList,flags); pStart = 0; while( pList && pList->zText[0]!=';' ){ pList = pList->pNext; |
︙ | ︙ | |||
2468 2469 2470 2471 2472 2473 2474 | pDecl->zDecl = StrDup(StringGet(&str), 0); StringReset(&str); pDecl->zExtra = 0; } /* ** Reset the DP_Forward and DP_Declared flags on all Decl structures. | | | 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 | pDecl->zDecl = StrDup(StringGet(&str), 0); StringReset(&str); pDecl->zExtra = 0; } /* ** Reset the DP_Forward and DP_Declared flags on all Decl structures. ** Set both flags for anything that is tagged as local and isn't ** in the file zFilename so that it won't be printing in other files. */ static void ResetDeclFlags(char *zFilename){ Decl *pDecl; for(pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext){ DeclClearProperty(pDecl,DP_Forward|DP_Declared); |
︙ | ︙ | |||
2571 2572 2573 2574 2575 2576 2577 | ){ Decl *p; /* The object to be declared */ int flag; int isCpp; /* True if generating C++ */ int doneTypedef = 0; /* True if a typedef has been done for this object */ /* printf("BEGIN %s of %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/ | | | 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 | ){ Decl *p; /* The object to be declared */ int flag; int isCpp; /* True if generating C++ */ int doneTypedef = 0; /* True if a typedef has been done for this object */ /* printf("BEGIN %s of %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/ /* ** For any object that has a forward declaration, go ahead and do the ** forward declaration first. */ isCpp = (pState->flags & DP_Cplusplus) != 0; for(p=pDecl; p; p=p->pSameName){ if( p->zFwd ){ if( !DeclHasProperty(p,DP_Forward) ){ |
︙ | ︙ | |||
2623 2624 2625 2626 2627 2628 2629 | ** with the DP_Flag bit. We are only able to use DP_Flag in this ** way because we know we'll never execute this far into this ** function on a recursive call with the same pDecl. Hence, recursive ** calls to this function (through ScanText()) can never change the ** value of DP_Flag out from under us. */ for(p=pDecl; p; p=p->pSameName){ | | | | 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 | ** with the DP_Flag bit. We are only able to use DP_Flag in this ** way because we know we'll never execute this far into this ** function on a recursive call with the same pDecl. Hence, recursive ** calls to this function (through ScanText()) can never change the ** value of DP_Flag out from under us. */ for(p=pDecl; p; p=p->pSameName){ if( !DeclHasProperty(p,DP_Declared) && (p->zFwd==0 || needFullDecl) && p->zDecl!=0 ){ DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag); }else{ DeclClearProperty(p,DP_Flag); } } |
︙ | ︙ | |||
2732 2733 2734 2735 2736 2737 2738 | /* ** See if there is a declaration in the database with the name given ** by sToken. */ pDecl = FindDecl(sToken.zText,sToken.nText); if( pDecl==0 ) continue; | | | | 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 | /* ** See if there is a declaration in the database with the name given ** by sToken. */ pDecl = FindDecl(sToken.zText,sToken.nText); if( pDecl==0 ) continue; /* ** If we get this far, we've found an identifier that has a ** declaration in the database. Now see if we the full declaration ** or just a forward declaration. */ GetNonspaceToken(&sIn,&sNext); if( sNext.zText[0]=='*' ){ needFullDecl = 0; }else{ |
︙ | ︙ | |||
2767 2768 2769 2770 2771 2772 2773 | static void CompleteForwardDeclarations(GenState *pState){ Decl *pDecl; int progress; do{ progress = 0; for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){ | | | | 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 | static void CompleteForwardDeclarations(GenState *pState){ Decl *pDecl; int progress; do{ progress = 0; for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){ if( DeclHasProperty(pDecl,DP_Forward) && !DeclHasProperty(pDecl,DP_Declared) ){ DeclareObject(pDecl,pState,1); progress = 1; assert( DeclHasProperty(pDecl,DP_Declared) ); } } }while( progress ); |
︙ | ︙ | |||
2839 2840 2841 2842 2843 2844 2845 | if( WriteFile(pFile->zHdr,zNewVersion) ){ fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr); nErr++; } }else if( report ){ fprintf(report,"unchanged\n"); } | | | 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | if( WriteFile(pFile->zHdr,zNewVersion) ){ fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr); nErr++; } }else if( report ){ fprintf(report,"unchanged\n"); } SafeFree(zOldVersion); IdentTableReset(&includeTable); StringReset(&outStr); return nErr; } /* ** Generate a global header file -- a header file that contains all |
︙ | ︙ | |||
2875 2876 2877 2878 2879 2880 2881 | DeclareObject(pDecl,&sState,1); } } ChangeIfContext(0,&sState); printf("%s",StringGet(&outStr)); IdentTableReset(&includeTable); StringReset(&outStr); | | | 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 | DeclareObject(pDecl,&sState,1); } } ChangeIfContext(0,&sState); printf("%s",StringGet(&outStr)); IdentTableReset(&includeTable); StringReset(&outStr); return 0; } #ifdef DEBUG /* ** Return the number of characters in the given string prior to the ** first newline. */ |
︙ | ︙ | |||
3037 3038 3039 3040 3041 3042 3043 | */ static InFile *CreateInFile(char *zArg, int *pnErr){ int nSrc; char *zSrc; InFile *pFile; int i; | | | 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 | */ static InFile *CreateInFile(char *zArg, int *pnErr){ int nSrc; char *zSrc; InFile *pFile; int i; /* ** Get the name of the input file to be scanned. The input file is ** everything before the first ':' or the whole file if no ':' is seen. ** ** Except, on windows, ignore any ':' that occurs as the second character ** since it might be part of the drive specifier. So really, the ":' has ** to be the 3rd or later character in the name. This precludes 1-character ** file names, which really should not be a problem. |
︙ | ︙ | |||
3096 3097 3098 3099 3100 3101 3102 | SafeFree(pFile->zHdr); pFile->zHdr = 0; } } /* ** If pFile->zSrc contains no 'c' or 'C' in its extension, it | | | | 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 | SafeFree(pFile->zHdr); pFile->zHdr = 0; } } /* ** If pFile->zSrc contains no 'c' or 'C' in its extension, it ** must be a header file. In that case, we need to set the ** PS_Interface flag. */ pFile->flags |= PS_Interface; for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){ if( zSrc[i]=='c' || zSrc[i]=='C' ){ pFile->flags &= ~PS_Interface; break; } } /* Done! */ return pFile; } /* MS-Windows and MS-DOS both have the following serious OS bug: the ** length of a command line is severely restricted. But this program ** occasionally requires long command lines. Hence the following |
︙ | ︙ | |||
3159 3160 3161 3162 3163 3164 3165 | } c = ' '; while( c!=EOF ){ while( c!=EOF && isspace(c) ){ if( c=='\n' ){ startOfLine = 1; } | | | 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 | } c = ' '; while( c!=EOF ){ while( c!=EOF && isspace(c) ){ if( c=='\n' ){ startOfLine = 1; } c = getc(in); if( startOfLine && c=='#' ){ while( c!=EOF && c!='\n' ){ c = getc(in); } } } n = 0; |
︙ | ︙ | |||
3181 3182 3183 3184 3185 3186 3187 | nNew++; if( nNew + argc > nAlloc ){ if( nAlloc==0 ){ nAlloc = 100 + argc; zNew = malloc( sizeof(char*) * nAlloc ); }else{ nAlloc *= 2; | | | 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 | nNew++; if( nNew + argc > nAlloc ){ if( nAlloc==0 ){ nAlloc = 100 + argc; zNew = malloc( sizeof(char*) * nAlloc ); }else{ nAlloc *= 2; zNew = realloc( zNew, sizeof(char*) * nAlloc ); } } if( zNew ){ int j = nNew + index; zNew[j] = malloc( n + 1 ); if( zNew[j] ){ strcpy( zNew[j], zBuf ); |
︙ | ︙ | |||
3251 3252 3253 3254 3255 3256 3257 | ); } /* ** The following text contains a few simple #defines that we want ** to be available to every file. */ | | | 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 | ); } /* ** The following text contains a few simple #defines that we want ** to be available to every file. */ static const char zInit[] = "#define INTERFACE 0\n" "#define EXPORT_INTERFACE 0\n" "#define LOCAL_INTERFACE 0\n" "#define EXPORT\n" "#define LOCAL static\n" "#define PUBLIC\n" "#define PRIVATE\n" |
︙ | ︙ |
Changes to src/makemake.tcl.
1 2 | #!/usr/bin/tclsh # | | | > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #!/usr/bin/tclsh # # Run this Tcl script to generate the various makefiles for a variety # of platforms. Files generated include: # # src/main.mk # makefile for all unix systems # win/Makefile.mingw # makefile for mingw on windows # win/Makefile.* # makefiles for other windows compilers # # Run this script while in the "src" subdirectory. Like this: # # tclsh makemake.tcl # ############################################################################# # Basenames of all source files that get preprocessed using # "translate" and "makeheaders". To add new C-language source files to the # project, simply add the basename to this list and rerun this script. # # Set the separate extra_files variable further down for how to add non-C # files, such as string and BLOB resources. # set src { add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat |
︙ | ︙ | |||
43 44 45 46 47 48 49 50 51 52 53 54 55 56 | diffcmd doc encode event export file finfo glob graph gzip http http_socket http_transport import | > > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | diffcmd doc encode event export file finfo foci fusefs glob graph gzip http http_socket http_transport import |
︙ | ︙ | |||
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | json_report json_status json_tag json_timeline json_user json_wiki leaf login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path pivot popen pqueue printf rebuild regexp report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winhttp wysiwyg xfer xfersetup zip http_ssl } # Name of the final application # set name fossil # The "writeln" command sends output to the target makefile. # proc writeln {args} { global output_file if {[lindex $args 0]=="-nonewline"} { puts -nonewline $output_file [lindex $args 1] } else { puts $output_file [lindex $args 0] } } # STOP HERE. # Unless the build procedures changes, you should not have to edit anything # below this line. ############################################################################## ############################################################################## ############################################################################## # Start by generating the "main.mk" makefile used for all unix systems. # puts "building main.mk" | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip http_ssl } # Additional resource files that get built into the executable. # set extra_files { diff.tcl markdown.md ../skins/*/*.txt } # Options used to compile the included SQLite library. # set SQLITE_OPTIONS { -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS } #lappend SQLITE_OPTIONS -DSQLITE_ENABLE_FTS3=1 #lappend SQLITE_OPTIONS -DSQLITE_ENABLE_STAT4 #lappend SQLITE_OPTIONS -DSQLITE_WIN32_NO_ANSI #lappend SQLITE_OPTIONS -DSQLITE_WINNT_MAX_PATH_CHARS=4096 # Options used to compile the included SQLite shell. # set SHELL_OPTIONS { -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open } # miniz (libz drop-in alternative) precompiler flags. # set MINIZ_OPTIONS { -DMINIZ_NO_STDIO -DMINIZ_NO_TIME -DMINIZ_NO_ARCHIVE_APIS } # Options used to compile the included SQLite shell on Windows. # set SHELL_WIN32_OPTIONS $SHELL_OPTIONS lappend SHELL_WIN32_OPTIONS -Daccess=file_access lappend SHELL_WIN32_OPTIONS -Dsystem=fossil_system lappend SHELL_WIN32_OPTIONS -Dgetenv=fossil_getenv lappend SHELL_WIN32_OPTIONS -Dfopen=fossil_fopen # Name of the final application # set name fossil # The "writeln" command sends output to the target makefile. # proc writeln {args} { global output_file if {[lindex $args 0]=="-nonewline"} { puts -nonewline $output_file [lindex $args 1] } else { puts $output_file [lindex $args 0] } } # STOP HERE. # Unless the build procedures changes, you should not have to edit anything # below this line. # Expand any wildcards in "extra_files" set new_extra_files {} foreach file $extra_files { foreach x [glob -nocomplain $file] { lappend new_extra_files $x } } set extra_files $new_extra_files ############################################################################## ############################################################################## ############################################################################## # Start by generating the "main.mk" makefile used for all unix systems. # puts "building main.mk" |
︙ | ︙ | |||
163 164 165 166 167 168 169 | # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # # This file is included by primary Makefile. # | | > > > > > | > > > > > > > > > > > > > > > > > > > > > | | | | | | | | > > | | > | | | < > | > > | > | | | | | | | | > | > > > > | | > | | | < < < < < < < < < | < < < | | | | < > > > | | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 | # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # # This file is included by primary Makefile. # XTCC = $(TCC) -I. -I$(SRCDIR) -I$(OBJDIR) $(TCCFLAGS) $(CFLAGS) } writeln -nonewline "SRC =" foreach s [lsort $src] { writeln -nonewline " \\\n \$(SRCDIR)/$s.c" } writeln "\n" writeln -nonewline "EXTRA_FILES =" foreach s [lsort $extra_files] { writeln -nonewline " \\\n \$(SRCDIR)/$s" } writeln "\n" writeln -nonewline "TRANS_SRC =" foreach s [lsort $src] { writeln -nonewline " \\\n \$(OBJDIR)/${s}_.c" } writeln "\n" writeln -nonewline "OBJ =" foreach s [lsort $src] { writeln -nonewline " \\\n \$(OBJDIR)/$s.o" } writeln "\n" writeln "APPNAME = $name\$(E)" writeln "\n" writeln [string map [list \ <<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \ <<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \ <<<MINIZ_OPTIONS>>> [join $MINIZ_OPTIONS " \\\n "]] { all: $(OBJDIR) $(APPNAME) install: $(APPNAME) mkdir -p $(INSTALLDIR) mv $(APPNAME) $(INSTALLDIR) codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 $(OBJDIR)/codecheck1 $(TRANS_SRC) $(OBJDIR): -mkdir $(OBJDIR) $(OBJDIR)/translate: $(SRCDIR)/translate.c $(BCC) -o $(OBJDIR)/translate $(SRCDIR)/translate.c $(OBJDIR)/makeheaders: $(SRCDIR)/makeheaders.c $(BCC) -o $(OBJDIR)/makeheaders $(SRCDIR)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR)/mkindex.c $(BCC) -o $(OBJDIR)/mkindex $(SRCDIR)/mkindex.c $(OBJDIR)/mkbuiltin: $(SRCDIR)/mkbuiltin.c $(BCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR)/mkbuiltin.c $(OBJDIR)/mkversion: $(SRCDIR)/mkversion.c $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c $(OBJDIR)/codecheck1: $(SRCDIR)/codecheck1.c $(BCC) -o $(OBJDIR)/codecheck1 $(SRCDIR)/codecheck1.c # WARNING. DANGER. Running the test suite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ $(SRCDIR)/../manifest \ $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h # Setup the options used to compile the included SQLite library. SQLITE_OPTIONS = <<<SQLITE_OPTIONS>>> # Setup the options used to compile the included SQLite shell. SHELL_OPTIONS = <<<SHELL_OPTIONS>>> # Setup the options used to compile the included miniz library. MINIZ_OPTIONS = <<<MINIZ_OPTIONS>>> # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set # to 1. If it is set to 1, then there is no need to build or link # the sqlite3.o object. Instead, the system SQLite will be linked # using -lsqlite3. SQLITE3_OBJ.1 = SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ. = $(SQLITE3_OBJ.0) # The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or # set to 1. If it is set to 1, the miniz library included in the # source tree should be used; otherwise, it should not. MINIZ_OBJ.0 = MINIZ_OBJ.1 = $(OBJDIR)/miniz.o MINIZ_OBJ. = $(MINIZ_OBJ.0) }] writeln [string map [list <<<NEXT_LINE>>> \\] { EXTRAOBJ = <<<NEXT_LINE>>> $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) <<<NEXT_LINE>>> $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) <<<NEXT_LINE>>> $(OBJDIR)/shell.o <<<NEXT_LINE>>> $(OBJDIR)/th.o <<<NEXT_LINE>>> $(OBJDIR)/th_lang.o <<<NEXT_LINE>>> $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>> $(OBJDIR)/cson_amalgamation.o }] writeln { $(APPNAME): $(OBJDIR)/headers $(OBJDIR)/codecheck1 $(OBJ) $(EXTRAOBJ) $(OBJDIR)/codecheck1 $(TRANS_SRC) $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) # This rule prevents make from using its default rules to try build # an executable named "manifest" out of the file named "manifest.c" # $(SRCDIR)/../manifest: # noop clean: rm -rf $(OBJDIR)/* $(APPNAME) } set mhargs {} foreach s [lsort $src] { append mhargs "\$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h <<<NEXT_LINE>>>" set extra_h($s) { } } append mhargs "\$(SRCDIR)/sqlite3.h <<<NEXT_LINE>>>" append mhargs "\$(SRCDIR)/th.h <<<NEXT_LINE>>>" #append mhargs "\$(SRCDIR)/cson_amalgamation.h <<<NEXT_LINE>>>" append mhargs "\$(OBJDIR)/VERSION.h" set mhargs [string map [list <<<NEXT_LINE>>> \\\n\t] $mhargs] writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >\$@\n" writeln "\$(OBJDIR)/builtin_data.h: \$(OBJDIR)/mkbuiltin \$(EXTRA_FILES)" writeln "\t\$(OBJDIR)/mkbuiltin --prefix \$(SRCDIR)/ \$(EXTRA_FILES) >\$@\n" writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h" writeln "\t\$(OBJDIR)/makeheaders $mhargs" writeln "\ttouch \$(OBJDIR)/headers" writeln "\$(OBJDIR)/headers: Makefile" writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/json_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_status.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h" writeln "Makefile:" set extra_h(main) " \$(OBJDIR)/page_index.h " set extra_h(builtin) " \$(OBJDIR)/builtin_data.h " foreach s [lsort $src] { writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate" writeln "\t\$(OBJDIR)/translate \$(SRCDIR)/$s.c >\$@\n" writeln "\$(OBJDIR)/$s.o:\t\$(OBJDIR)/${s}_.c \$(OBJDIR)/$s.h$extra_h($s)\$(SRCDIR)/config.h" writeln "\t\$(XTCC) -o \$(OBJDIR)/$s.o -c \$(OBJDIR)/${s}_.c\n" writeln "\$(OBJDIR)/$s.h:\t\$(OBJDIR)/headers\n" } writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c" writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) -c \$(SRCDIR)/sqlite3.c -o \$@\n" writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) -c \$(SRCDIR)/shell.c -o \$@\n" writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n" writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n" writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c" writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n" writeln { $(OBJDIR)/miniz.o: $(SRCDIR)/miniz.c $(XTCC) $(MINIZ_OPTIONS) -c $(SRCDIR)/miniz.c -o $@ $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $@ # # The list of all the targets that do not correspond to real files. This stops # 'make' from getting confused when someone makes an error in a rule. # .PHONY: all install test clean |
︙ | ︙ | |||
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | #### Enable JSON (http://www.json.org) support using "cson" # # FOSSIL_ENABLE_JSON = 1 #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # # FOSSIL_ENABLE_TCL = 1 #### Load Tcl using the stubs library mechanism # # FOSSIL_ENABLE_TCL_STUBS = 1 #### Load Tcl using the private stubs mechanism # # FOSSIL_ENABLE_TCL_PRIVATE_STUBS = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### Check if the workaround for the MinGW command line handling needs to | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 | #### Enable JSON (http://www.json.org) support using "cson" # # FOSSIL_ENABLE_JSON = 1 #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # # FOSSIL_ENABLE_SSL = 1 #### Automatically build OpenSSL when building Fossil (causes rebuild # issues when building incrementally). # # FOSSIL_BUILD_SSL = 1 #### Enable TH1 scripts in embedded documentation files # # FOSSIL_ENABLE_TH1_DOCS = 1 #### Enable hooks for commands and web pages via TH1 # # FOSSIL_ENABLE_TH1_HOOKS = 1 #### Enable scripting support via Tcl/Tk # # FOSSIL_ENABLE_TCL = 1 #### Load Tcl using the stubs library mechanism # # FOSSIL_ENABLE_TCL_STUBS = 1 #### Load Tcl using the private stubs mechanism # # FOSSIL_ENABLE_TCL_PRIVATE_STUBS = 1 #### Use 'system' SQLite # # USE_SYSTEM_SQLITE = 1 #### Use the miniz compression library # # FOSSIL_ENABLE_MINIZ = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### Check if the workaround for the MinGW command line handling needs to # be enabled by default. This check may be somewhat fragile due to the # use of "findstring". # ifndef MINGW_IS_32BIT_ONLY ifeq (,$(findstring w64-mingw32,$(PREFIX))) MINGW_IS_32BIT_ONLY = 1 endif endif #### The directories where the zlib include and library files are located. # ZINCDIR = $(SRCDIR)/../compat/zlib ZLIBDIR = $(SRCDIR)/../compat/zlib #### Make an attempt to detect if Fossil is being built for the x64 processor # architecture. This check may be somewhat fragile due to "findstring". # ifndef X64 ifneq (,$(findstring x86_64-w64-mingw32,$(PREFIX))) X64 = 1 endif endif #### Determine if the optimized assembly routines provided with zlib should be # used, taking into account whether zlib is actually enabled and the target # processor architecture. # ifndef X64 SSLCONFIG = mingw ifndef FOSSIL_ENABLE_MINIZ ZLIBCONFIG = LOC="-DASMV -DASMINF" OBJA="inffas86.o match.o" LIBTARGETS = $(ZLIBDIR)/inffas86.o $(ZLIBDIR)/match.o else ZLIBCONFIG = LIBTARGETS = endif else SSLCONFIG = mingw64 ZLIBCONFIG = LIBTARGETS = endif #### Disable creation of the OpenSSL shared libraries. Also, disable support # for both SSLv2 and SSLv3 (i.e. thereby forcing the use of TLS). # SSLCONFIG += no-ssl2 no-ssl3 no-shared #### When using zlib, make sure that OpenSSL is configured to use the zlib # that Fossil knows about (i.e. the one within the source tree). # ifndef FOSSIL_ENABLE_MINIZ SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2a OPENSSLINCDIR = $(OPENSSLDIR)/include OPENSSLLIBDIR = $(OPENSSLDIR) #### Either the directory where the Tcl library is installed or the Tcl # source code directory resides (depending on the value of the macro # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, # this directory must have "include" and "lib" sub-directories. If # this points to the Tcl source code directory, this directory must # have "generic" and "win" sub-directories. The recommended usage |
︙ | ︙ | |||
467 468 469 470 471 472 473 | #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # | | > > > > > > | > > > > > > > > > > | | > > > > > > > > > > > > | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 | #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # TCC = $(PREFIX)gcc -Os -Wall #### When not using the miniz compression library, zlib is required. # ifndef FOSSIL_ENABLE_MINIZ TCC += -L$(ZLIBDIR) -I$(ZINCDIR) endif #### Add the necessary command line options to build with debugging # symbols, if enabled. # ifdef FOSSIL_ENABLE_SYMBOLS TCC += -g endif #### Compile resources for use in building executables that will run # on the target platform. # RCC = $(PREFIX)windres -I$(SRCDIR) ifndef FOSSIL_ENABLE_MINIZ RCC += -I$(ZINCDIR) endif # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -L$(OPENSSLLIBDIR) -I$(OPENSSLINCDIR) RCC += -I$(OPENSSLINCDIR) endif # With Tcl support ifdef FOSSIL_ENABLE_TCL ifdef FOSSIL_TCL_SOURCE TCC += -L$(TCLSRCDIR)/win -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win RCC += -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win else TCC += -L$(TCLLIBDIR) -I$(TCLINCDIR) RCC += -I$(TCLINCDIR) endif endif # With miniz (i.e. instead of zlib) ifdef FOSSIL_ENABLE_MINIZ TCC += -DFOSSIL_ENABLE_MINIZ=1 RCC += -DFOSSIL_ENABLE_MINIZ=1 endif # With MinGW command line handling workaround ifdef MINGW_IS_32BIT_ONLY TCC += -DBROKEN_MINGW_CMDLINE=1 RCC += -DBROKEN_MINGW_CMDLINE=1 endif # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -DFOSSIL_ENABLE_SSL=1 RCC += -DFOSSIL_ENABLE_SSL=1 endif # With TH1 embedded docs support ifdef FOSSIL_ENABLE_TH1_DOCS TCC += -DFOSSIL_ENABLE_TH1_DOCS=1 RCC += -DFOSSIL_ENABLE_TH1_DOCS=1 endif # With TH1 hook support ifdef FOSSIL_ENABLE_TH1_HOOKS TCC += -DFOSSIL_ENABLE_TH1_HOOKS=1 RCC += -DFOSSIL_ENABLE_TH1_HOOKS=1 endif # With Tcl support ifdef FOSSIL_ENABLE_TCL TCC += -DFOSSIL_ENABLE_TCL=1 RCC += -DFOSSIL_ENABLE_TCL=1 # Either statically linked or via stubs ifdef FOSSIL_ENABLE_TCL_STUBS |
︙ | ︙ | |||
539 540 541 542 543 544 545 | endif #### We add the -static option here so that we can build a static # executable that will run in a chroot jail. # LIB = -static | | > > > > > > > | > | > | > > > > > > | 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | endif #### We add the -static option here so that we can build a static # executable that will run in a chroot jail. # LIB = -static #### MinGW: If available, use the Unicode capable runtime startup code. # ifndef MINGW_IS_32BIT_ONLY LIB += -municode endif #### SQLite: If enabled, use the system SQLite library. # ifdef USE_SYSTEM_SQLITE LIB += -lsqlite3 endif #### OpenSSL: Add the necessary libraries required, if enabled. # ifdef FOSSIL_ENABLE_SSL LIB += -lssl -lcrypto -lgdi32 endif #### Tcl: Add the necessary libraries required, if enabled. # ifdef FOSSIL_ENABLE_TCL LIB += $(LIBTCL) endif #### Extra arguments for linking the finished binary. Fossil needs # to link against the Z-Lib compression library. There are no # other mandatory dependencies. # LIB += -lmingwex #### When not using the miniz compression library, zlib is required. # ifndef FOSSIL_ENABLE_MINIZ LIB += -lz endif #### These libraries MUST appear in the same order as they do for Tcl # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL ifdef FOSSIL_ENABLE_TCL_STUBS LIB += -lkernel32 -lws2_32 |
︙ | ︙ | |||
580 581 582 583 584 585 586 | #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location # | | > > > > > > > > > | > | | | | > > > > | | | | > > > > > > > > | | | | | | > > > | | > > > | | > > > > | | > > > > > > > > > > > > > > | | | > | | < < < > | > > > > > > > > > > > | | > | > > > > > > | | > > > | | | > > > > | | > | | | > > > > > > > > > > > > > > > > | < | < | | < < | | | < | | | > > | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 | #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location # MAKENSIS = "$(PROGRAMFILES)\NSIS\MakeNSIS.exe" #### Inno Setup executable location # INNOSETUP = "$(PROGRAMFILES)\Inno Setup 5\ISCC.exe" #### Include a configuration file that can override any one of these settings. # -include config.w32 # STOP HERE # You should not need to change anything below this line #-------------------------------------------------------- XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) } writeln -nonewline "SRC =" foreach s [lsort $src] { writeln -nonewline " \\\n \$(SRCDIR)/$s.c" } writeln "\n" writeln -nonewline "EXTRA_FILES =" foreach s [lsort $extra_files] { writeln -nonewline " \\\n \$(SRCDIR)/$s" } writeln "\n" writeln -nonewline "TRANS_SRC =" foreach s [lsort $src] { writeln -nonewline " \\\n \$(OBJDIR)/${s}_.c" } writeln "\n" writeln -nonewline "OBJ =" foreach s [lsort $src] { writeln -nonewline " \\\n \$(OBJDIR)/$s.o" } writeln "\n" writeln "APPNAME = ${name}.exe" writeln "APPTARGETS =" writeln { #### If the USE_WINDOWS variable exists, it is assumed that we are building # inside of a Windows-style shell; otherwise, it is assumed that we are # building inside of a Unix-style shell. Note that the "move" command is # broken when attempting to use it from the Windows shell via MinGW make # because the SHELL variable is only used for certain commands that are # recognized internally by make. # ifdef USE_WINDOWS TRANSLATE = $(subst /,\,$(OBJDIR)/translate.exe) MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe) MKINDEX = $(subst /,\,$(OBJDIR)/mkindex.exe) MKBUILTIN = $(subst /,\,$(OBJDIR)/mkbuiltin.exe) MKVERSION = $(subst /,\,$(OBJDIR)/mkversion.exe) CODECHECK1 = $(subst /,\,$(OBJDIR)/codecheck1.exe) CAT = type CP = copy GREP = find MV = copy RM = del /Q MKDIR = -mkdir RMDIR = rmdir /S /Q else TRANSLATE = $(OBJDIR)/translate.exe MAKEHEADERS = $(OBJDIR)/makeheaders.exe MKINDEX = $(OBJDIR)/mkindex.exe MKBUILTIN = $(OBJDIR)/mkbuiltin.exe MKVERSION = $(OBJDIR)/mkversion.exe CODECHECK1 = $(OBJDIR)/codecheck1.exe CAT = cat CP = cp GREP = grep MV = mv RM = rm -f MKDIR = -mkdir -p RMDIR = rm -rf endif} writeln { all: $(OBJDIR) $(APPNAME) $(OBJDIR)/fossil.o: $(SRCDIR)/../win/fossil.rc $(OBJDIR)/VERSION.h ifdef USE_WINDOWS $(CAT) $(subst /,\,$(SRCDIR)\miniz.c) | $(GREP) "define MZ_VERSION" > $(subst /,\,$(OBJDIR)\minizver.h) $(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.rc) $(subst /,\,$(OBJDIR)) $(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.ico) $(subst /,\,$(OBJDIR)) $(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.exe.manifest) $(subst /,\,$(OBJDIR)) else $(CAT) $(SRCDIR)/miniz.c | $(GREP) "define MZ_VERSION" > $(OBJDIR)/minizver.h $(CP) $(SRCDIR)/../win/fossil.rc $(OBJDIR) $(CP) $(SRCDIR)/../win/fossil.ico $(OBJDIR) $(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR) endif $(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o install: $(OBJDIR) $(APPNAME) ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(INSTALLDIR)) $(MV) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR)) else $(MKDIR) $(INSTALLDIR) $(MV) $(APPNAME) $(INSTALLDIR) endif $(OBJDIR): ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(OBJDIR)) else $(MKDIR) $(OBJDIR) endif $(TRANSLATE): $(SRCDIR)/translate.c $(BCC) -o $@ $(SRCDIR)/translate.c $(MAKEHEADERS): $(SRCDIR)/makeheaders.c $(BCC) -o $@ $(SRCDIR)/makeheaders.c $(MKINDEX): $(SRCDIR)/mkindex.c $(BCC) -o $@ $(SRCDIR)/mkindex.c $(MKBUILTIN): $(SRCDIR)/mkbuiltin.c $(BCC) -o $@ $(SRCDIR)/mkbuiltin.c $(MKVERSION): $(SRCDIR)/mkversion.c $(BCC) -o $@ $(SRCDIR)/mkversion.c $(CODECHECK1): $(SRCDIR)/codecheck1.c $(BCC) -o $@ $(SRCDIR)/codecheck1.c # WARNING. DANGER. Running the test suite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set # to 1. If it is set to 1, then there is no need to build or link # the sqlite3.o object. Instead, the system SQLite will be linked # using -lsqlite3. SQLITE3_OBJ.1 = SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ. = $(SQLITE3_OBJ.0) # The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or # set to 1. If it is set to 1, the miniz library included in the # source tree should be used; otherwise, it should not. MINIZ_OBJ.0 = MINIZ_OBJ.1 = $(OBJDIR)/miniz.o MINIZ_OBJ. = $(MINIZ_OBJ.0) } writeln [string map [list <<<NEXT_LINE>>> \\] { EXTRAOBJ = <<<NEXT_LINE>>> $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) <<<NEXT_LINE>>> $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) <<<NEXT_LINE>>> $(OBJDIR)/shell.o <<<NEXT_LINE>>> $(OBJDIR)/th.o <<<NEXT_LINE>>> $(OBJDIR)/th_lang.o <<<NEXT_LINE>>> $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>> $(OBJDIR)/cson_amalgamation.o }] writeln { zlib: $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) $(ZLIBCONFIG) -f win32/Makefile.gcc libz.a clean-zlib: $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc clean $(ZLIBDIR)/inffas86.o: $(TCC) -c -o $@ -DASMINF -I$(ZLIBDIR) -O3 $(ZLIBDIR)/contrib/inflate86/inffas86.c $(ZLIBDIR)/match.o: $(TCC) -c -o $@ -DASMV $(ZLIBDIR)/contrib/asm686/match.S ifndef FOSSIL_ENABLE_MINIZ LIBTARGETS += zlib endif openssl: $(LIBTARGETS) cd $(OPENSSLLIBDIR);./Configure --cross-compile-prefix=$(PREFIX) $(SSLCONFIG) $(MAKE) -C $(OPENSSLLIBDIR) build_libs clean-openssl: $(MAKE) -C $(OPENSSLLIBDIR) clean tcl: cd $(TCLSRCDIR)/win;./configure $(MAKE) -C $(TCLSRCDIR)/win $(TCLTARGET) clean-tcl: $(MAKE) -C $(TCLSRCDIR)/win distclean APPTARGETS += $(LIBTARGETS) ifdef FOSSIL_BUILD_SSL APPTARGETS += openssl endif $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(CODECHECK1) $(TRANS_SRC) $(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o # This rule prevents make from using its default rules to try build # an executable named "manifest" out of the file named "manifest.c" # $(SRCDIR)/../manifest: # noop clean: ifdef USE_WINDOWS $(RM) $(subst /,\,$(APPNAME)) $(RMDIR) $(subst /,\,$(OBJDIR)) else $(RM) $(APPNAME) $(RMDIR) $(OBJDIR) endif setup: $(OBJDIR) $(APPNAME) $(MAKENSIS) ./setup/fossil.nsi innosetup: $(OBJDIR) $(APPNAME) $(INNOSETUP) ./setup/fossil.iss -DAppVersion=$(shell $(CAT) ./VERSION) } set mhargs {} foreach s [lsort $src] { if {[string length $mhargs] > 0} {append mhargs " \\\n\t\t"} append mhargs "\$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h" set extra_h($s) { } } append mhargs " \\\n\t\t\$(SRCDIR)/sqlite3.h" append mhargs " \\\n\t\t\$(SRCDIR)/th.h" append mhargs " \\\n\t\t\$(OBJDIR)/VERSION.h" writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(MKINDEX)" writeln "\t\$(MKINDEX) \$(TRANS_SRC) >\$@\n" writeln "\$(OBJDIR)/builtin_data.h:\t\$(MKBUILTIN) \$(EXTRA_FILES)" writeln "\t\$(MKBUILTIN) --prefix \$(SRCDIR)/ \$(EXTRA_FILES) >\$@\n" writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(MAKEHEADERS) \$(OBJDIR)/VERSION.h" writeln "\t\$(MAKEHEADERS) $mhargs" writeln "\techo Done >\$(OBJDIR)/headers\n" writeln "\$(OBJDIR)/headers: Makefile\n" writeln "Makefile:\n" set extra_h(main) " \$(OBJDIR)/page_index.h " set extra_h(builtin) " \$(OBJDIR)/builtin_data.h " foreach s [lsort $src] { writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(TRANSLATE)" writeln "\t\$(TRANSLATE) \$(SRCDIR)/$s.c >\$@\n" writeln "\$(OBJDIR)/$s.o:\t\$(OBJDIR)/${s}_.c \$(OBJDIR)/$s.h$extra_h($s)\$(SRCDIR)/config.h" writeln "\t\$(XTCC) -o \$(OBJDIR)/$s.o -c \$(OBJDIR)/${s}_.c\n" writeln "\$(OBJDIR)/${s}.h:\t\$(OBJDIR)/headers\n" } set SQLITE_WIN32_OPTIONS $SQLITE_OPTIONS lappend SQLITE_WIN32_OPTIONS -DSQLITE_WIN32_NO_ANSI set MINGW_SQLITE_OPTIONS $SQLITE_WIN32_OPTIONS lappend MINGW_SQLITE_OPTIONS -D_HAVE__MINGW_H lappend MINGW_SQLITE_OPTIONS -DSQLITE_USE_MALLOC_H lappend MINGW_SQLITE_OPTIONS -DSQLITE_USE_MSIZE set MINIZ_WIN32_OPTIONS $MINIZ_OPTIONS set j " \\\n " writeln "SQLITE_OPTIONS = [join $MINGW_SQLITE_OPTIONS $j]\n" set j " \\\n " writeln "SHELL_OPTIONS = [join $SHELL_WIN32_OPTIONS $j]\n" set j " \\\n " writeln "MINIZ_OPTIONS = [join $MINIZ_WIN32_OPTIONS $j]\n" writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c \$(SRCDIR)/../win/Makefile.mingw" writeln "\t\$(XTCC) \$(SQLITE_OPTIONS) \$(SQLITE_CFLAGS) -c \$(SRCDIR)/sqlite3.c -o \$@\n" writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c" writeln "\t\$(XTCC) -c \$(SRCDIR)/cson_amalgamation.c -o \$@\n" writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_config.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_dir.o \$(OBJDIR)/jsos_finfo.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_status.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h\n" writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h \$(SRCDIR)/../win/Makefile.mingw" writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) -c \$(SRCDIR)/shell.c -o \$@\n" writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n" writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n" writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c" writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n" writeln "\$(OBJDIR)/miniz.o:\t\$(SRCDIR)/miniz.c" writeln "\t\$(XTCC) \$(MINIZ_OPTIONS) -c \$(SRCDIR)/miniz.c -o \$@\n" close $output_file # # End of the win/Makefile.mingw output ############################################################################## ############################################################################## ############################################################################## |
︙ | ︙ | |||
832 833 834 835 836 837 838 | SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 } | | > | | > | 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 | SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 } writeln "SQLITE_OPTIONS = [join $SQLITE_OPTIONS { }]\n" writeln "SHELL_OPTIONS = [join $SHELL_WIN32_OPTIONS { }]\n" writeln -nonewline "SRC = " foreach s [lsort $src] { writeln -nonewline "${s}_.c " } writeln "\n" writeln -nonewline "OBJ = " foreach s [lsort $src] { writeln -nonewline "\$(OBJDIR)\\$s\$O " } writeln "\$(OBJDIR)\\shell\$O \$(OBJDIR)\\sqlite3\$O \$(OBJDIR)\\th\$O \$(OBJDIR)\\th_lang\$O " writeln { RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E codecheck1$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) codecheck1$E $(SRC) $(DMDIR)\bin\link @link $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res} writeln -nonewline "\t+echo " |
︙ | ︙ | |||
881 882 883 884 885 886 887 | makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** | > > > | > > > | | | | > > > | | 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 | makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** mkbuiltin$E: $(SRCDIR)\mkbuiltin.c $(BCC) -o$@ $** mkversion$E: $(SRCDIR)\mkversion.c $(BCC) -o$@ $** codecheck1$E: $(SRCDIR)\codecheck1.c $(BCC) -o$@ $** $(OBJDIR)\shell$O : $(SRCDIR)\shell.c $(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) -o$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $** $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) -o$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) -o$@ -c $** $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h cp $@ $@ VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION +$** > $@ page_index.h: mkindex$E $(SRC) +$** > $@ builtin_data.h: mkbuiltin$E $(EXTRA_FILES) mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ clean: -del $(OBJDIR)\*.obj -del *.obj *_.c *.h *.map realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E codecheck1$E mkbuiltin$E $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h |
︙ | ︙ | |||
937 938 939 940 941 942 943 | foreach s [lsort $src] { writeln "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h" writeln "\t\$(TCC) -o\$@ -c ${s}_.c\n" writeln "${s}_.c : \$(SRCDIR)\\$s.c" writeln "\t+translate\$E \$** > \$@\n" } | | | 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 | foreach s [lsort $src] { writeln "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h" writeln "\t\$(TCC) -o\$@ -c ${s}_.c\n" writeln "${s}_.c : \$(SRCDIR)\\$s.c" writeln "\t+translate\$E \$** > \$@\n" } writeln -nonewline "headers: makeheaders\$E page_index.h builtin_data.h VERSION.h\n\t +makeheaders\$E " foreach s [lsort $src] { writeln -nonewline "${s}_.c:$s.h " } writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h" writeln "\t@copy /Y nul: headers" close $output_file |
︙ | ︙ | |||
960 961 962 963 964 965 966 967 968 969 970 971 | set output_file [open ../win/Makefile.msc w] fconfigure $output_file -translation binary writeln {# ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl") ############################################################################## # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # | > > > > > > > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | > > > > > | > > | > > > > > > > > > > > > > > > > > > > > | | | | | > > > > > > > > > > | | | | | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > | | > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > | > | > > | | > < > > > | > > > | | | | > > > > > > | | > > > | | | | > | | | | | | > | > | > | > | > > > > > < | | | 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 | set output_file [open ../win/Makefile.msc w] fconfigure $output_file -translation binary writeln {# ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl") ############################################################################## # # This Makefile will only function correctly if used from a sub-directory # that is a direct child of the top-level directory for this project. # !if !exist("..\.fossil-settings") !error "Please change the current directory to the one containing this file." !endif # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # B = .. SRCDIR = $B\src OBJDIR = . OX = . O = .obj E = .exe P = .pdb # Perl is only necessary if OpenSSL support is enabled and it must # be built from source code. The PERLDIR variable should point to # the directory containing the main Perl binary (i.e. "perl.exe"). PERLDIR = C:\Perl\bin PERL = perl.exe # Uncomment to enable debug symbols # DEBUG = 1 # Uncomment to support Windows XP with Visual Studio 201x # FOSSIL_ENABLE_WINXP = 1 # Uncomment to enable JSON API # FOSSIL_ENABLE_JSON = 1 # Uncomment to enable miniz usage # FOSSIL_ENABLE_MINIZ = 1 # Uncomment to enable SSL support # FOSSIL_ENABLE_SSL = 1 # Uncomment to build SSL libraries # FOSSIL_BUILD_SSL = 1 # Uncomment to enable TH1 scripts in embedded documentation files # FOSSIL_ENABLE_TH1_DOCS = 1 # Uncomment to enable TH1 hooks # FOSSIL_ENABLE_TH1_HOOKS = 1 # Uncomment to enable Tcl support # FOSSIL_ENABLE_TCL = 1 !ifdef FOSSIL_ENABLE_SSL SSLDIR = $(B)\compat\openssl-1.0.2a SSLINCDIR = $(SSLDIR)\inc32 SSLLIBDIR = $(SSLDIR)\out32 SSLLFLAGS = /nologo /opt:ref /debug SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" !message Using 'x64' platform for OpenSSL... # BUGBUG (OpenSSL): Apparently, using "no-ssl*" here breaks the build. # SSLCONFIG = VC-WIN64A no-asm no-ssl2 no-ssl3 no-shared SSLCONFIG = VC-WIN64A no-asm no-shared SSLSETUP = ms\do_win64a.bat SSLNMAKE = ms\nt.mak all SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 !elseif "$(PLATFORM)"=="ia64" !message Using 'ia64' platform for OpenSSL... # BUGBUG (OpenSSL): Apparently, using "no-ssl*" here breaks the build. # SSLCONFIG = VC-WIN64I no-asm no-ssl2 no-ssl3 no-shared SSLCONFIG = VC-WIN64I no-asm no-shared SSLSETUP = ms\do_win64i.bat SSLNMAKE = ms\nt.mak all SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 !else !message Assuming 'x86' platform for OpenSSL... # BUGBUG (OpenSSL): Apparently, using "no-ssl*" here breaks the build. # SSLCONFIG = VC-WIN32 no-asm no-ssl2 no-ssl3 no-shared SSLCONFIG = VC-WIN32 no-asm no-shared SSLSETUP = ms\do_ms.bat SSLNMAKE = ms\nt.mak all SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 !endif !endif !ifdef FOSSIL_ENABLE_TCL TCLDIR = $(B)\compat\tcl-8.6 TCLSRCDIR = $(TCLDIR) TCLINCDIR = $(TCLSRCDIR)\generic !endif # zlib options ZINCDIR = $(B)\compat\zlib ZLIBDIR = $(B)\compat\zlib ZLIB = zlib.lib INCL = /I. /I$(SRCDIR) /I$B\win\include !ifndef FOSSIL_ENABLE_MINIZ INCL = $(INCL) /I$(ZINCDIR) !endif !ifdef FOSSIL_ENABLE_SSL INCL = $(INCL) /I$(SSLINCDIR) !endif !ifdef FOSSIL_ENABLE_TCL INCL = $(INCL) /I$(TCLINCDIR) !endif CFLAGS = /nologo LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO !ifdef FOSSIL_ENABLE_WINXP XPCFLAGS = $(XPCFLAGS) /D_USING_V110_SDK71_=1 CFLAGS = $(CFLAGS) $(XPCFLAGS) !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" XPLDFLAGS = $(XPLDFLAGS) /SUBSYSTEM:CONSOLE,5.02 !else XPLDFLAGS = $(XPLDFLAGS) /SUBSYSTEM:CONSOLE,5.01 !endif LDFLAGS = $(LDFLAGS) $(XPLDFLAGS) !endif !ifdef DEBUG CFLAGS = $(CFLAGS) /Zi /MTd /Od LDFLAGS = $(LDFLAGS) /DEBUG !else CFLAGS = $(CFLAGS) /MT /O2 !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = rc /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) LIBS = ws2_32.lib advapi32.lib LIBDIR = !ifndef FOSSIL_ENABLE_MINIZ LIBS = $(LIBS) $(ZLIB) LIBDIR = $(LIBDIR) /LIBPATH:$(ZLIBDIR) !endif !ifdef FOSSIL_ENABLE_MINIZ TCC = $(TCC) /DFOSSIL_ENABLE_MINIZ=1 RCC = $(RCC) /DFOSSIL_ENABLE_MINIZ=1 !endif !ifdef FOSSIL_ENABLE_JSON TCC = $(TCC) /DFOSSIL_ENABLE_JSON=1 RCC = $(RCC) /DFOSSIL_ENABLE_JSON=1 !endif !ifdef FOSSIL_ENABLE_SSL TCC = $(TCC) /DFOSSIL_ENABLE_SSL=1 RCC = $(RCC) /DFOSSIL_ENABLE_SSL=1 LIBS = $(LIBS) $(SSLLIB) LIBDIR = $(LIBDIR) /LIBPATH:$(SSLLIBDIR) !endif !ifdef FOSSIL_ENABLE_TH1_DOCS TCC = $(TCC) /DFOSSIL_ENABLE_TH1_DOCS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TH1_DOCS=1 !endif !ifdef FOSSIL_ENABLE_TH1_HOOKS TCC = $(TCC) /DFOSSIL_ENABLE_TH1_HOOKS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TH1_HOOKS=1 !endif !ifdef FOSSIL_ENABLE_TCL TCC = $(TCC) /DFOSSIL_ENABLE_TCL=1 RCC = $(RCC) /DFOSSIL_ENABLE_TCL=1 TCC = $(TCC) /DFOSSIL_ENABLE_TCL_STUBS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TCL_STUBS=1 TCC = $(TCC) /DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 TCC = $(TCC) /DUSE_TCL_STUBS=1 RCC = $(RCC) /DUSE_TCL_STUBS=1 !endif } regsub -all {[-]D} [join $SQLITE_WIN32_OPTIONS { }] {/D} MSC_SQLITE_OPTIONS set j " \\\n " writeln "SQLITE_OPTIONS = [join $MSC_SQLITE_OPTIONS $j]\n" regsub -all {[-]D} [join $SHELL_WIN32_OPTIONS { }] {/D} MSC_SHELL_OPTIONS set j " \\\n " writeln "SHELL_OPTIONS = [join $MSC_SHELL_OPTIONS $j]\n" regsub -all {[-]D} [join $MINIZ_WIN32_OPTIONS { }] {/D} MSC_MINIZ_OPTIONS set j " \\\n " writeln "MINIZ_OPTIONS = [join $MSC_MINIZ_OPTIONS $j]\n" writeln -nonewline "SRC = " set i 0 foreach s [lsort $src] { if {$i > 0} { writeln " \\" writeln -nonewline " " } writeln -nonewline "${s}_.c"; incr i } writeln "\n" writeln -nonewline "EXTRA_FILES = " set i 0 foreach s [lsort $extra_files] { if {$i > 0} { writeln " \\" writeln -nonewline " " } writeln -nonewline "\$(SRCDIR)\\${s}"; incr i } writeln "\n" set AdditionalObj [list shell sqlite3 th th_lang th_tcl cson_amalgamation] writeln -nonewline "OBJ = " set i 0 foreach s [lsort [concat $src $AdditionalObj]] { if {$i > 0} { writeln " \\" writeln -nonewline " " } writeln -nonewline "\$(OX)\\$s\$O"; incr i } if {$i > 0} { writeln " \\" } writeln "!ifdef FOSSIL_ENABLE_MINIZ" writeln -nonewline " " writeln "\$(OX)\\miniz\$O \\"; incr i writeln "!endif" writeln -nonewline " \$(OX)\\fossil.res\n\n" writeln { APPNAME = $(OX)\fossil$(E) PDBNAME = $(OX)\fossil$(P) APPTARGETS = all: $(OX) $(APPNAME) zlib: @echo Building zlib from "$(ZLIBDIR)"... !ifdef FOSSIL_ENABLE_WINXP @pushd "$(ZLIBDIR)" && $(MAKE) /f win32\Makefile.msc $(ZLIB) "CC=cl $(XPCFLAGS)" "LD=link $(XPLDFLAGS)" && popd !else @pushd "$(ZLIBDIR)" && $(MAKE) /f win32\Makefile.msc $(ZLIB) && popd !endif !ifdef FOSSIL_ENABLE_SSL openssl: @echo Building OpenSSL from "$(SSLDIR)"... !if "$(PERLDIR)" != "" @set PATH=$(PERLDIR);$(PATH) !endif @pushd "$(SSLDIR)" && $(PERL) Configure $(SSLCONFIG) && popd @pushd "$(SSLDIR)" && call $(SSLSETUP) && popd !ifdef FOSSIL_ENABLE_WINXP @pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) "CC=cl $(SSLCFLAGS) $(XPCFLAGS)" "LFLAGS=$(SSLLFLAGS) $(XPLDFLAGS)" && popd !else @pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) "CC=cl $(SSLCFLAGS)" && popd !endif !endif !ifndef FOSSIL_ENABLE_MINIZ APPTARGETS = $(APPTARGETS) zlib !endif !ifdef FOSSIL_ENABLE_SSL !ifdef FOSSIL_BUILD_SSL APPTARGETS = $(APPTARGETS) openssl !endif !endif $(APPNAME) : $(APPTARGETS) translate$E mkindex$E codecheck1$E headers $(OBJ) $(OX)\linkopts cd $(OX) codecheck1$E $(SRC) link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts $(OX)\linkopts: $B\win\Makefile.msc} set redir {>} foreach s [lsort [concat $src $AdditionalObj]] { writeln "\techo \$(OX)\\$s.obj $redir \$@" set redir {>>} } set redir {>>} writeln "!ifdef FOSSIL_ENABLE_MINIZ" writeln "\techo \$(OX)\\miniz.obj $redir \$@" writeln "!endif" writeln "\techo \$(LIBS) $redir \$@" writeln { $(OX): @-mkdir $@ translate$E: $(SRCDIR)\translate.c $(BCC) $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) $** mkbuiltin$E: $(SRCDIR)\mkbuiltin.c $(BCC) $** mkversion$E: $(SRCDIR)\mkversion.c $(BCC) $** codecheck1$E: $(SRCDIR)\codecheck1.c $(BCC) $** $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c $(OX)\th$O : $(SRCDIR)\th.c $(TCC) /Fo$@ -c $** $(OX)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** $(OX)\th_tcl$O : $(SRCDIR)\th_tcl.c $(TCC) /Fo$@ -c $** $(OX)\miniz$O : $(SRCDIR)\miniz.c $(TCC) /Fo$@ -c $(MINIZ_OPTIONS) $(SRCDIR)\miniz.c VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION $** > $@ $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c $(TCC) /Fo$@ /c $** page_index.h: mkindex$E $(SRC) $** > $@ builtin_data.h: mkbuiltin$E $(EXTRA_FILES) mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ clean: del $(OX)\*.obj 2>NUL del *.obj 2>NUL del *_.c 2>NUL del *.h 2>NUL del *.ilk 2>NUL del *.map 2>NUL del *.res 2>NUL del headers 2>NUL del linkopts 2>NUL del vc*.pdb 2>NUL realclean: clean del $(APPNAME) 2>NUL del $(PDBNAME) 2>NUL del translate$E 2>NUL del translate$P 2>NUL del mkindex$E 2>NUL del mkindex$P 2>NUL del makeheaders$E 2>NUL del makeheaders$P 2>NUL del mkversion$E 2>NUL del mkversion$P 2>NUL del codecheck1$E 2>NUL del codecheck1$P 2>NUL del mkbuiltin$E 2>NUL del mkbuiltin$P 2>NUL $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_finfo$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_status$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h } foreach s [lsort $src] { writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" writeln "${s}_.c : \$(SRCDIR)\\$s.c" writeln "\ttranslate\$E \$** > \$@\n" } writeln "fossil.res : \$B\\win\\fossil.rc" writeln "\t\$(RCC) /fo \$@ \$**\n" writeln "headers: makeheaders\$E page_index.h builtin_data.h VERSION.h" writeln -nonewline "\tmakeheaders\$E " set i 0 foreach s [lsort $src] { if {$i > 0} { writeln " \\" writeln -nonewline "\t\t\t" } |
︙ | ︙ | |||
1171 1172 1173 1174 1175 1176 1177 | ############################################################################## # Begin win/Makefile.PellesCGMake output # puts "building ../win/Makefile.PellesCGMake" set output_file [open ../win/Makefile.PellesCGMake w] fconfigure $output_file -translation binary | | > > | 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 | ############################################################################## # Begin win/Makefile.PellesCGMake output # puts "building ../win/Makefile.PellesCGMake" set output_file [open ../win/Makefile.PellesCGMake w] fconfigure $output_file -translation binary writeln [string map [list \ <<<SQLITE_OPTIONS>>> [join $SQLITE_WIN32_OPTIONS { }] \ <<<SHELL_OPTIONS>>> [join $SHELL_WIN32_OPTIONS { }]] {# ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl") ############################################################################## # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. |
︙ | ︙ | |||
1205 1206 1207 1208 1209 1210 1211 | # zlib sources 1.2.5 # Windows XP SP 2 # and # PellesC 6.00.4 # gmake 3.80 # zlib sources 1.2.5 # Windows 7 Home Premium | | | | 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 | # zlib sources 1.2.5 # Windows XP SP 2 # and # PellesC 6.00.4 # gmake 3.80 # zlib sources 1.2.5 # Windows 7 Home Premium # # PellesCDir=c:\Programme\PellesC # Select between 32/64 bit code, default is 32 bit #TARGETVERSION=64 ifeq ($(TARGETVERSION),64) # 64 bit version |
︙ | ︙ | |||
1250 1251 1252 1253 1254 1255 1256 | # define commands for building the windows resource files RESOURCE=fossil.res RC=$(PellesCDir)\bin\porc.exe RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION) # define the special utilities files, needed to generate # the automatically generated source files | | | | | | | 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 | # define commands for building the windows resource files RESOURCE=fossil.res RC=$(PellesCDir)\bin\porc.exe RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION) # define the special utilities files, needed to generate # the automatically generated source files UTILS=translate.exe mkindex.exe makeheaders.exe mkbuiltin.exe UTILS_OBJ=$(UTILS:.exe=.obj) UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c)) # define the SQLite files, which need special flags on compile SQLITESRC=sqlite3.c ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf)) SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj)) SQLITEDEFINES=<<<SQLITE_OPTIONS>>> # define the SQLite shell files, which need special flags on compile SQLITESHELLSRC=shell.c ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf)) SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj)) SQLITESHELLDEFINES=<<<SHELL_OPTIONS>>> # define the th scripting files, which need special flags on compile THSRC=th.c th_lang.c ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf)) THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj)) # define the zlib files, needed by this compile |
︙ | ︙ | |||
1289 1290 1291 1292 1293 1294 1295 | TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj) # main target file is the application APPLICATION=fossil.exe # define the standard make target .PHONY: default | | | 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 | TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj) # main target file is the application APPLICATION=fossil.exe # define the standard make target .PHONY: default default: page_index.h builtin_data.h headers $(APPLICATION) # symbolic target to generate the source generate utils .PHONY: utils utils: $(UTILS) # link utils $(UTILS) version.exe: %.exe: %.obj |
︙ | ︙ | |||
1315 1316 1317 1318 1319 1320 1321 1322 1323 | $(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe translate.exe $< >$@ # generate the index source, containing all web references,.. page_index.h: $(TRANSLATEDSRC) mkindex.exe mkindex.exe $(TRANSLATEDSRC) >$@ # extracting version info from manifest VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION | > > > | | | 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 | $(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe translate.exe $< >$@ # generate the index source, containing all web references,.. page_index.h: $(TRANSLATEDSRC) mkindex.exe mkindex.exe $(TRANSLATEDSRC) >$@ builtin_data.h: $(EXTRA_FILES) mkbuiltin.exe mkbuiltin.exe --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ # extracting version info from manifest VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION version.exe ..\manifest.uuid ..\manifest ..\VERSION >$@ # generate the simplified headers headers: makeheaders.exe page_index.h builtin_data.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h echo Done >$@ # compile C sources with relevant options $(TRANSLATEDOBJ): %_.obj: %_.c %.h $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
︙ | ︙ | |||
1361 1362 1363 1364 1365 1366 1367 | del /F $(TRANSLATEDSRC) del /F *.h headers del /F $(RESOURCE) .PHONY: clobber clobber: clean del /F *.exe | | | 1880 1881 1882 1883 1884 1885 1886 1887 | del /F $(TRANSLATEDSRC) del /F *.h headers del /F $(RESOURCE) .PHONY: clobber clobber: clean del /F *.exe }] |
Changes to src/manifest.c.
︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 | /* ** File permissions used by Fossil internally. */ #define PERM_REG 0 /* regular file */ #define PERM_EXE 1 /* executable */ #define PERM_LNK 2 /* symlink */ /* ** A single F-card within a manifest */ | > > > > > > > | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | /* ** File permissions used by Fossil internally. */ #define PERM_REG 0 /* regular file */ #define PERM_EXE 1 /* executable */ #define PERM_LNK 2 /* symlink */ /* ** Flags for use with manifest_crosslink(). */ #define MC_NONE 0 /* default handling */ #define MC_PERMIT_HOOKS 1 /* permit hooks to execute */ #define MC_NO_ERRORS 2 /* do not issue errors for a bad parse */ /* ** A single F-card within a manifest */ struct ManifestFile { char *zName; /* Name of a file */ char *zUuid; /* UUID of the file */ char *zPerm; /* File permissions */ char *zPrior; /* Prior name if the name was changed */ }; |
︙ | ︙ | |||
81 82 83 84 85 86 87 | int nFileAlloc; /* Slots allocated in aFile[] */ int iFile; /* Index of current file in iterator */ ManifestFile *aFile; /* One entry for each F-card */ int nParent; /* Number of parents. */ int nParentAlloc; /* Slots allocated in azParent[] */ char **azParent; /* UUIDs of parents. One for each P card argument */ int nCherrypick; /* Number of entries in aCherrypick[] */ | | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | int nFileAlloc; /* Slots allocated in aFile[] */ int iFile; /* Index of current file in iterator */ ManifestFile *aFile; /* One entry for each F-card */ int nParent; /* Number of parents. */ int nParentAlloc; /* Slots allocated in azParent[] */ char **azParent; /* UUIDs of parents. One for each P card argument */ int nCherrypick; /* Number of entries in aCherrypick[] */ struct { char *zCPTarget; /* UUID of cherry-picked version w/ +|- prefix */ char *zCPBase; /* UUID of cherry-pick baseline. NULL for singletons */ } *aCherrypick; int nCChild; /* Number of cluster children */ int nCChildAlloc; /* Number of closts allocated in azCChild[] */ char **azCChild; /* UUIDs of referenced objects in a cluster. M cards */ int nTag; /* Number of T Cards */ int nTagAlloc; /* Slots allocated in aTag[] */ struct TagType { char *zName; /* Name of the tag */ char *zUuid; /* UUID that the tag is applied to */ char *zValue; /* Value if the tag is really a property */ } *aTag; /* One for each T card */ int nField; /* Number of J cards */ int nFieldAlloc; /* Slots allocated in aField[] */ struct { char *zName; /* Key or field name */ char *zValue; /* Value of the field */ } *aField; /* One for each J card */ }; #endif /* |
︙ | ︙ | |||
127 128 129 130 131 132 133 | /* ** Clear the memory allocated in a manifest object */ void manifest_destroy(Manifest *p){ if( p ){ blob_reset(&p->content); | | | | | | | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | /* ** Clear the memory allocated in a manifest object */ void manifest_destroy(Manifest *p){ if( p ){ blob_reset(&p->content); fossil_free(p->aFile); fossil_free(p->azParent); fossil_free(p->azCChild); fossil_free(p->aTag); fossil_free(p->aField); fossil_free(p->aCherrypick); if( p->pBaseline ) manifest_destroy(p->pBaseline); memset(p, 0, sizeof(*p)); fossil_free(p); } } /* |
︙ | ︙ | |||
221 222 223 224 225 226 227 | /* ** Remove the PGP signature from the artifact, if there is one. */ static void remove_pgp_signature(char **pz, int *pn){ char *z = *pz; int n = *pn; int i; | | | | | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | /* ** Remove the PGP signature from the artifact, if there is one. */ static void remove_pgp_signature(char **pz, int *pn){ char *z = *pz; int n = *pn; int i; if( strncmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return; for(i=34; i<n && !after_blank_line(z+i); i++){} if( i>=n ) return; z += i; n -= i; *pz = z; for(i=n-1; i>=0; i--){ if( z[i]=='\n' && strncmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){ n = i+1; break; } } *pn = n; return; } /* ** Verify the Z-card checksum on the artifact, if there is such a ** checksum. Return 0 if there is no Z-card. Return 1 if the Z-card ** exists and is correct. Return 2 if the Z-card exists and has the wrong ** value. ** ** 0123456789 123456789 123456789 123456789 ** Z aea84f4f863865a8d59d0384e4d2a41c */ static int verify_z_card(const char *z, int n){ if( n<35 ) return 0; if( z[n-35]!='Z' || z[n-34]!=' ' ) return 0; md5sum_init(); md5sum_step_text(z, n-35); |
︙ | ︙ | |||
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | char cPrevType = 0; char cType; char *z; int n; char *zUuid; int sz = 0; int isRepeat, hasSelfRefTag = 0; static Bag seen; const char *zErr = 0; if( rid==0 ){ isRepeat = 1; }else if( bag_find(&seen, rid) ){ isRepeat = 1; }else{ isRepeat = 0; bag_insert(&seen, rid); } /* Every control artifact ends with a '\n' character. Exit early ** if that is not the case for this artifact. */ if( !isRepeat ) g.parseCnt[0]++; z = blob_materialize(pContent); n = blob_size(pContent); | > | | | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 | char cPrevType = 0; char cType; char *z; int n; char *zUuid; int sz = 0; int isRepeat, hasSelfRefTag = 0; Blob bUuid = BLOB_INITIALIZER; static Bag seen; const char *zErr = 0; if( rid==0 ){ isRepeat = 1; }else if( bag_find(&seen, rid) ){ isRepeat = 1; }else{ isRepeat = 0; bag_insert(&seen, rid); } /* Every control artifact ends with a '\n' character. Exit early ** if that is not the case for this artifact. */ if( !isRepeat ) g.parseCnt[0]++; z = blob_materialize(pContent); n = blob_size(pContent); if( pErr && (n<=0 || z[n-1]!='\n') ){ blob_reset(pContent); blob_append(pErr, n ? "not terminated with \\n" : "zero-length", -1); return 0; } /* Strip off the PGP signature if there is one. */ remove_pgp_signature(&z, &n); |
︙ | ︙ | |||
395 396 397 398 399 400 401 402 403 404 405 406 407 408 | /* Then verify the Z-card. */ if( verify_z_card(z, n)==2 ){ blob_reset(pContent); blob_appendf(pErr, "incorrect Z-card cksum"); return 0; } /* Allocate a Manifest object to hold the parsed control artifact. */ p = fossil_malloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); memcpy(&p->content, pContent, sizeof(p->content)); p->rid = rid; | > > > > > | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 | /* Then verify the Z-card. */ if( verify_z_card(z, n)==2 ){ blob_reset(pContent); blob_appendf(pErr, "incorrect Z-card cksum"); return 0; } /* Store the UUID (before modifying the blob) only for error ** reporting purposes. */ sha1sum_blob(pContent, &bUuid); /* Allocate a Manifest object to hold the parsed control artifact. */ p = fossil_malloc( sizeof(*p) ); memset(p, 0, sizeof(*p)); memcpy(&p->content, pContent, sizeof(p->content)); p->rid = rid; |
︙ | ︙ | |||
427 428 429 430 431 432 433 | */ case 'A': { char *zName, *zTarget, *zSrc; int nTarget = 0, nSrc = 0; zName = next_token(&x, 0); zTarget = next_token(&x, &nTarget); zSrc = next_token(&x, &nSrc); | | | 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | */ case 'A': { char *zName, *zTarget, *zSrc; int nTarget = 0, nSrc = 0; zName = next_token(&x, 0); zTarget = next_token(&x, &nTarget); zSrc = next_token(&x, &nSrc); if( zName==0 || zTarget==0 ) goto manifest_syntax_error; if( p->zAttachName!=0 ) goto manifest_syntax_error; defossilize(zName); if( !file_is_simple_pathname(zName, 0) ){ SYNTAX("invalid filename on A-card"); } defossilize(zTarget); if( (nTarget!=UUID_SIZE || !validate16(zTarget, UUID_SIZE)) |
︙ | ︙ | |||
496 497 498 499 500 501 502 | if( p->rDate<=0.0 ) SYNTAX("cannot parse date on D-card"); break; } /* ** E <timestamp> <uuid> ** | | | 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | if( p->rDate<=0.0 ) SYNTAX("cannot parse date on D-card"); break; } /* ** E <timestamp> <uuid> ** ** An "event" card that contains the timestamp of the event in the ** format YYYY-MM-DDtHH:MM:SS and a unique identifier for the event. ** The event timestamp is distinct from the D timestamp. The D ** timestamp is when the artifact was created whereas the E timestamp ** is when the specific event is said to occur. */ case 'E': { if( p->rEventDate>0.0 ) SYNTAX("more than one E-card"); |
︙ | ︙ | |||
543 544 545 546 547 548 549 | defossilize(zPriorName); if( !file_is_simple_pathname(zPriorName, 0) ){ SYNTAX("F-card old filename is not a simple path"); } } if( p->nFile>=p->nFileAlloc ){ p->nFileAlloc = p->nFileAlloc*2 + 10; | | | 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 | defossilize(zPriorName); if( !file_is_simple_pathname(zPriorName, 0) ){ SYNTAX("F-card old filename is not a simple path"); } } if( p->nFile>=p->nFileAlloc ){ p->nFileAlloc = p->nFileAlloc*2 + 10; p->aFile = fossil_realloc(p->aFile, p->nFileAlloc*sizeof(p->aFile[0]) ); } i = p->nFile++; p->aFile[i].zName = zName; p->aFile[i].zUuid = zUuid; p->aFile[i].zPerm = zPerm; p->aFile[i].zPrior = zPriorName; |
︙ | ︙ | |||
663 664 665 666 667 668 669 | /* ** P <uuid> ... ** ** Specify one or more other artifacts which are the parents of ** this artifact. The first parent is the primary parent. All ** others are parents by merge. Note that the initial empty | | | | | 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 | /* ** P <uuid> ... ** ** Specify one or more other artifacts which are the parents of ** this artifact. The first parent is the primary parent. All ** others are parents by merge. Note that the initial empty ** check-in historically has an empty P-card, so empty P-cards ** must be accepted. */ case 'P': { while( (zUuid = next_token(&x, &sz))!=0 ){ if( sz!=UUID_SIZE ) SYNTAX("wrong size UUID on P-card"); if( !validate16(zUuid, UUID_SIZE) )SYNTAX("invalid UUID on P-card"); if( p->nParent>=p->nParentAlloc ){ p->nParentAlloc = p->nParentAlloc*2 + 5; p->azParent = fossil_realloc(p->azParent, p->nParentAlloc*sizeof(char*)); } i = p->nParent++; p->azParent[i] = zUuid; } break; } /* ** Q (+|-)<uuid> ?<uuid>? ** ** Specify one or a range of check-ins that are cherrypicked into ** this check-in ("+") or backed out of this check-in ("-"). */ case 'Q': { if( (zUuid=next_token(&x, &sz))==0 ) SYNTAX("missing UUID on Q-card"); if( sz!=UUID_SIZE+1 ) SYNTAX("wrong size UUID on Q-card"); if( zUuid[0]!='+' && zUuid[0]!='-' ){ SYNTAX("Q-card does not begin with '+' or '-'"); } |
︙ | ︙ | |||
735 736 737 738 739 740 741 | ** Create or cancel a tag or property. The tagname is fossil-encoded. ** The first character of the name must be either "+" to create a ** singleton tag, "*" to create a propagating tag, or "-" to create ** anti-tag that undoes a prior "+" or blocks propagation of of ** a "*". ** ** The tag is applied to <uuid>. If <uuid> is "*" then the tag is | | | 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 | ** Create or cancel a tag or property. The tagname is fossil-encoded. ** The first character of the name must be either "+" to create a ** singleton tag, "*" to create a propagating tag, or "-" to create ** anti-tag that undoes a prior "+" or blocks propagation of of ** a "*". ** ** The tag is applied to <uuid>. If <uuid> is "*" then the tag is ** applied to the current manifest. If <value> is provided then ** the tag is really a property with the given value. ** ** Tags are not allowed in clusters. Multiple T lines are allowed. */ case 'T': { char *zName, *zValue; zName = next_token(&x, 0); |
︙ | ︙ | |||
882 883 884 885 886 887 888 889 890 | || p->zWiki ){ SYNTAX("cluster contains a card other than M- or Z-"); } if( !seenZ ) SYNTAX("missing Z-card on cluster"); p->type = CFTYPE_CLUSTER; }else if( p->zEventId ){ if( p->rDate<=0.0 ) SYNTAX("missing date on event"); if( p->nFile>0 ) SYNTAX("F-card in event"); | > > < < > > > > | > > > > > > > > > | | | > > < < < < > < < < < < < < < < | | < < | | < > > > > > | 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 | || p->zWiki ){ SYNTAX("cluster contains a card other than M- or Z-"); } if( !seenZ ) SYNTAX("missing Z-card on cluster"); p->type = CFTYPE_CLUSTER; }else if( p->zEventId ){ if( p->zAttachName ) SYNTAX("A-card in event"); if( p->zBaseline ) SYNTAX("B-card in event"); if( p->rDate<=0.0 ) SYNTAX("missing date on event"); if( p->nFile>0 ) SYNTAX("F-card in event"); if( p->nField>0 ) SYNTAX("J-card in event"); if( p->zTicketUuid ) SYNTAX("K-card in event"); if( p->zWikiTitle!=0 ) SYNTAX("L-card in event"); if( p->zRepoCksum ) SYNTAX("R-card in event"); if( p->zWiki==0 ) SYNTAX("missing W-card on event"); if( !seenZ ) SYNTAX("missing Z-card on event"); p->type = CFTYPE_EVENT; }else if( p->zWiki!=0 || p->zWikiTitle!=0 ){ if( p->zAttachName ) SYNTAX("A-card in wiki"); if( p->zBaseline ) SYNTAX("B-card in wiki"); if( p->rDate<=0.0 ) SYNTAX("missing date on wiki"); if( p->nFile>0 ) SYNTAX("F-card in wiki"); if( p->nField>0 ) SYNTAX("J-card in wiki"); if( p->zTicketUuid ) SYNTAX("K-card in wiki"); if( p->zWikiTitle==0 ) SYNTAX("missing L-card on wiki"); if( p->zRepoCksum ) SYNTAX("R-card in wiki"); if( p->nTag>0 ) SYNTAX("T-card in wiki"); if( p->zWiki==0 ) SYNTAX("missing W-card on wiki"); if( !seenZ ) SYNTAX("missing Z-card on wiki"); p->type = CFTYPE_WIKI; }else if( hasSelfRefTag || p->nFile>0 || p->zRepoCksum!=0 || p->zBaseline || p->nParent>0 ){ if( p->zAttachName ) SYNTAX("A-card in manifest"); if( p->rDate<=0.0 ) SYNTAX("missing date on manifest"); if( p->nField>0 ) SYNTAX("J-card in manifest"); if( p->zTicketUuid ) SYNTAX("K-card in manifest"); p->type = CFTYPE_MANIFEST; }else if( p->nField>0 || p->zTicketUuid!=0 ){ if( p->zAttachName ) SYNTAX("A-card in ticket"); if( p->rDate<=0.0 ) SYNTAX("missing date on ticket"); if( p->nField==0 ) SYNTAX("missing J-card on ticket"); if( p->zTicketUuid==0 ) SYNTAX("missing K-card on ticket"); if( p->zMimetype) SYNTAX("N-card in ticket"); if( p->nTag>0 ) SYNTAX("T-card in ticket"); if( p->zUser==0 ) SYNTAX("missing U-card on ticket"); if( !seenZ ) SYNTAX("missing Z-card on ticket"); p->type = CFTYPE_TICKET; }else if( p->zAttachName ){ if( p->rDate<=0.0 ) SYNTAX("missing date on attachment"); if( p->nTag>0 ) SYNTAX("T-card in attachment"); if( !seenZ ) SYNTAX("missing Z-card on attachment"); p->type = CFTYPE_ATTACHMENT; }else{ if( p->rDate<=0.0 ) SYNTAX("missing date on control"); if( p->zMimetype ) SYNTAX("N-card in control"); if( !seenZ ) SYNTAX("missing Z-card on control"); p->type = CFTYPE_CONTROL; } md5sum_init(); if( !isRepeat ) g.parseCnt[p->type]++; blob_reset(&bUuid); return p; manifest_syntax_error: if(bUuid.nUsed){ blob_appendf(pErr, "manifest [%.40s] ", blob_str(&bUuid)); blob_reset(&bUuid); } if( zErr ){ blob_appendf(pErr, "line %d: %s", lineNo, zErr); }else{ blob_appendf(pErr, "unknown error on line %d", lineNo); } md5sum_init(); manifest_destroy(p); |
︙ | ︙ | |||
975 976 977 978 979 980 981 | manifest_destroy(p); p = 0; } return p; } /* | | | | | 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 | manifest_destroy(p); p = 0; } return p; } /* ** Given a check-in name, load and parse the manifest for that check-in. ** Throw a fatal error if anything goes wrong. */ Manifest *manifest_get_by_name(const char *zName, int *pRid){ int rid; Manifest *p; rid = name_to_typed_rid(zName, "ci"); if( !is_a_version(rid) ){ fossil_fatal("no such check-in: %s", zName); } if( pRid ) *pRid = rid; p = manifest_get(rid, CFTYPE_MANIFEST, 0); if( p==0 ){ fossil_fatal("cannot parse manifest for check-in: %s", zName); } return p; } /* ** COMMAND: test-parse-manifest ** |
︙ | ︙ | |||
1042 1043 1044 1045 1046 1047 1048 | if( p->pBaseline==0 ){ if( !throwError ){ db_multi_exec( "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)", p->rid, rid ); return 1; | | | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 | if( p->pBaseline==0 ){ if( !throwError ){ db_multi_exec( "INSERT OR IGNORE INTO orphan(rid, baseline) VALUES(%d,%d)", p->rid, rid ); return 1; } fossil_fatal("cannot access baseline manifest %S", p->zBaseline); } } return 0; } /* |
︙ | ︙ | |||
1067 1068 1069 1070 1071 1072 1073 | /* ** Advance to the next manifest-file. ** ** Return NULL for end-of-records or if there is an error. If an error ** occurs and pErr!=0 then store 1 in *pErr. */ ManifestFile *manifest_file_next( | | | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 | /* ** Advance to the next manifest-file. ** ** Return NULL for end-of-records or if there is an error. If an error ** occurs and pErr!=0 then store 1 in *pErr. */ ManifestFile *manifest_file_next( Manifest *p, int *pErr ){ ManifestFile *pOut = 0; if( pErr ) *pErr = 0; if( p->pBaseline==0 ){ /* Manifest p is a baseline-manifest. Just scan down the list ** of files. */ |
︙ | ︙ | |||
1153 1154 1155 1156 1157 1158 1159 | /* ** Compute an appropriate mlink.mperm integer for the permission string ** of a file. */ int manifest_file_mperm(ManifestFile *pFile){ int mperm = PERM_REG; if( pFile && pFile->zPerm){ | | | > | | > | > | 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 | /* ** Compute an appropriate mlink.mperm integer for the permission string ** of a file. */ int manifest_file_mperm(ManifestFile *pFile){ int mperm = PERM_REG; if( pFile && pFile->zPerm){ if( strstr(pFile->zPerm,"x")!=0 ){ mperm = PERM_EXE; }else if( strstr(pFile->zPerm,"l")!=0 ){ mperm = PERM_LNK; } } return mperm; } /* ** Add a single entry to the mlink table. Also add the filename to ** the filename table if it is not there already. */ static void add_one_mlink( int pmid, /* The parent manifest */ const char *zFromUuid, /* UUID for content in parent */ int mid, /* The record ID of the manifest */ const char *zToUuid, /* UUID for content in child */ const char *zFilename, /* Filename */ const char *zPrior, /* Previous filename. NULL if unchanged */ int isPublic, /* True if mid is not a private manifest */ int isPrimary, /* pmid is the primary parent of mid */ int mperm /* 1: exec, 2: symlink */ ){ int fnid, pfnid, pid, fid; static Stmt s1; fnid = filename_to_fnid(zFilename); if( zPrior==0 ){ |
︙ | ︙ | |||
1195 1196 1197 1198 1199 1200 1201 | if( zToUuid==0 || zToUuid[0]==0 ){ fid = 0; }else{ fid = uuid_to_rid(zToUuid, 1); if( isPublic ) content_make_public(fid); } db_static_prepare(&s1, | | | | | > > | | > > > > | 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 | if( zToUuid==0 || zToUuid[0]==0 ){ fid = 0; }else{ fid = uuid_to_rid(zToUuid, 1); if( isPublic ) content_make_public(fid); } db_static_prepare(&s1, "INSERT INTO mlink(mid,fid,pmid,pid,fnid,pfnid,mperm,isaux)" "VALUES(:m,:f,:pm,:p,:n,:pfn,:mp,:isaux)" ); db_bind_int(&s1, ":m", mid); db_bind_int(&s1, ":f", fid); db_bind_int(&s1, ":pm", pmid); db_bind_int(&s1, ":p", pid); db_bind_int(&s1, ":n", fnid); db_bind_int(&s1, ":pfn", pfnid); db_bind_int(&s1, ":mp", mperm); db_bind_int(&s1, ":isaux", isPrimary==0); db_exec(&s1); if( pid && fid ){ content_deltify(pid, fid, 0); } } /* ** Do a binary search to find a file in the p->aFile[] array. ** ** As an optimization, guess that the file we seek is at index p->iFile. ** That will usually be the case. If it is not found there, then do the ** actual binary search. ** ** Update p->iFile to be the index of the file that is found. */ static ManifestFile *manifest_file_seek_base( Manifest *p, /* Manifest to search */ const char *zName, /* Name of the file we are looking for */ int bBest /* 0: exact match only. 1: closest match */ ){ int lwr, upr; int c; int i; lwr = 0; upr = p->nFile - 1; if( p->iFile>=lwr && p->iFile<upr ){ c = fossil_strcmp(p->aFile[p->iFile+1].zName, zName); |
︙ | ︙ | |||
1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 | }else if( c>0 ){ upr = i-1; }else{ p->iFile = i; return &p->aFile[i]; } } return 0; } /* ** Locate a file named zName in the aFile[] array of the given manifest. ** Return a pointer to the appropriate ManifestFile object. Return NULL ** if not found. ** | > > > > > | | | | | | | 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 | }else if( c>0 ){ upr = i-1; }else{ p->iFile = i; return &p->aFile[i]; } } if( bBest ){ if( lwr>=p->nFile ) lwr = p->nFile-1; i = (int)strlen(zName); if( strncmp(zName, p->aFile[lwr].zName, i)==0 ) return &p->aFile[lwr]; } return 0; } /* ** Locate a file named zName in the aFile[] array of the given manifest. ** Return a pointer to the appropriate ManifestFile object. Return NULL ** if not found. ** ** This routine works even if p is a delta-manifest. The pointer ** returned might be to the baseline. ** ** We assume that filenames are in sorted order and use a binary search. */ ManifestFile *manifest_file_seek(Manifest *p, const char *zName, int bBest){ ManifestFile *pFile; pFile = manifest_file_seek_base(p, zName, p->zBaseline ? 0 : bBest); if( pFile && pFile->zUuid==0 ) return 0; if( pFile==0 && p->zBaseline ){ fetch_baseline(p, 1); pFile = manifest_file_seek_base(p->pBaseline, zName,bBest); } return pFile; } /* ** Look for a file in a manifest, taking the case-sensitive option ** into account. If case-sensitive is off, then files in any case ** will match. */ ManifestFile *manifest_file_find(Manifest *p, const char *zName){ int i; Manifest *pBase; if( filenames_are_case_sensitive() ){ return manifest_file_seek(p, zName, 0); } for(i=0; i<p->nFile; i++){ if( fossil_stricmp(zName, p->aFile[i].zName)==0 ){ return &p->aFile[i]; } } if( p->zBaseline==0 ) return 0; |
︙ | ︙ | |||
1312 1313 1314 1315 1316 1317 1318 | ** A single mlink entry is added for every file that changed content, ** name, and/or permissions going from pid to cid. ** ** Deleted files have mlink.fid=0. ** Added files have mlink.pid=0. ** Edited files have both mlink.pid!=0 and mlink.fid!=0 */ | | > > > > | | | | > | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 | ** A single mlink entry is added for every file that changed content, ** name, and/or permissions going from pid to cid. ** ** Deleted files have mlink.fid=0. ** Added files have mlink.pid=0. ** Edited files have both mlink.pid!=0 and mlink.fid!=0 */ static void add_mlink( int pmid, Manifest *pParent, /* Parent check-in */ int mid, Manifest *pChild, /* The child check-in */ int isPrim /* TRUE if pmid is the primary parent of mid */ ){ Blob otherContent; int otherRid; int i, rc; ManifestFile *pChildFile, *pParentFile; Manifest **ppOther; static Stmt eq; int isPublic; /* True if pChild is non-private */ /* If mlink table entires are already exist for the pmid-to-mid transition, ** then abort early doing no work. */ db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid AND pmid=:pmid"); db_bind_int(&eq, ":mid", mid); db_bind_int(&eq, ":pmid", pmid); rc = db_step(&eq); db_reset(&eq); if( rc==SQLITE_ROW ) return; /* Compute the value of the missing pParent or pChild parameter. ** Fetch the baseline check-ins for both. */ assert( pParent==0 || pChild==0 ); if( pParent==0 ){ ppOther = &pParent; otherRid = pmid; }else{ ppOther = &pChild; otherRid = mid; } if( (*ppOther = manifest_cache_find(otherRid))==0 ){ content_get(otherRid, &otherContent); if( blob_size(&otherContent)==0 ) return; *ppOther = manifest_parse(&otherContent, otherRid, 0); if( *ppOther==0 ) return; } if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ manifest_destroy(*ppOther); return; } isPublic = !content_is_private(mid); /* Try to make the parent manifest a delta from the child, if that ** is an appropriate thing to do. For a new baseline, make the ** previous baseline a delta from the current baseline. */ if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){ content_deltify(pmid, mid, 0); }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ content_deltify(pParent->pBaseline->rid, mid, 0); } /* Remember all children less than a few seconds younger than their parent, ** as we might want to fudge the times for those children. */ if( pChild->rDate<pParent->rDate+AGE_FUDGE_WINDOW && manifest_crosslink_busy ){ db_multi_exec( "INSERT OR REPLACE INTO time_fudge VALUES(%d, %.17g, %d, %.17g);", pParent->rid, pParent->rDate, pChild->rid, pChild->rDate ); } /* First look at all files in pChild, ignoring its baseline. This ** is where most of the changes will be found. */ for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){ int mperm = manifest_file_mperm(pChildFile); if( pChildFile->zPrior ){ pParentFile = manifest_file_seek(pParent, pChildFile->zPrior, 0); if( pParentFile ){ /* File with name change */ add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid, pChildFile->zName, pChildFile->zPrior, isPublic, isPrim, mperm); }else{ /* File name changed, but the old name is not found in the parent! ** Treat this like a new file. */ add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0, isPublic, isPrim, mperm); } }else{ pParentFile = manifest_file_seek(pParent, pChildFile->zName, 0); if( pParentFile==0 ){ if( pChildFile->zUuid ){ /* A new file */ add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0, isPublic, isPrim, mperm); } }else if( fossil_strcmp(pChildFile->zUuid, pParentFile->zUuid)!=0 || manifest_file_mperm(pParentFile)!=mperm ){ /* Changes in file content or permissions */ add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid, pChildFile->zName, 0, isPublic, isPrim, mperm); } } } if( pParent->zBaseline && pChild->zBaseline ){ /* Both parent and child are delta manifests. Look for files that ** are deleted or modified in the parent but which reappear or revert ** to baseline in the child and show such files as being added or changed ** in the child. */ for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ if( pParentFile->zUuid ){ pChildFile = manifest_file_seek_base(pChild, pParentFile->zName, 0); if( pChildFile==0 ){ /* The child file reverts to baseline. Show this as a change */ pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); if( pChildFile ){ add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid, pChildFile->zName, 0, isPublic, isPrim, manifest_file_mperm(pChildFile)); } } }else{ pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); if( pChildFile ){ /* File resurrected in the child after having been deleted in ** the parent. Show this as an added file. */ add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0, isPublic, isPrim, manifest_file_mperm(pChildFile)); } } } }else if( pChild->zBaseline==0 ){ /* pChild is a baseline. Look for files that are present in pParent ** but are missing from pChild and mark them as having been deleted. */ manifest_file_rewind(pParent); while( (pParentFile = manifest_file_next(pParent,0))!=0 ){ pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); if( pChildFile==0 && pParentFile->zUuid!=0 ){ add_one_mlink(pmid, pParentFile->zUuid, mid, 0, pParentFile->zName, 0, isPublic, isPrim, 0); } } } manifest_cache_insert(*ppOther); } /* |
︙ | ︙ | |||
1470 1471 1472 1473 1474 1475 1476 | " cid INTEGER," /* A child or mid */ " m2 REAL" /* Timestamp on the child */ ");" ); } #if INTERFACE | | | > > > > > > > > > > > > | 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 | " cid INTEGER," /* A child or mid */ " m2 REAL" /* Timestamp on the child */ ");" ); } #if INTERFACE /* Timestamps might be adjusted slightly to ensure that check-ins appear ** on the timeline in chronological order. This is the maximum amount ** of the adjustment window, in days. */ #define AGE_FUDGE_WINDOW (2.0/86400.0) /* 2 seconds */ /* This is increment (in days) by which timestamps are adjusted for ** use on the timeline. */ #define AGE_ADJUST_INCREMENT (25.0/86400000.0) /* 25 milliseconds */ #endif /* LOCAL_INTERFACE */ /* ** Finish up a sequence of manifest_crosslink calls. */ int manifest_crosslink_end(int flags){ Stmt q, u; int i; int rc = TH_OK; int permitHooks = (flags & MC_PERMIT_HOOKS); const char *zScript = 0; assert( manifest_crosslink_busy==1 ); if( permitHooks ){ rc = xfer_run_common_script(); if( rc==TH_OK ){ zScript = xfer_ticket_code(); } } db_prepare(&q, "SELECT uuid FROM pending_tkt"); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); ticket_rebuild_entry(zUuid); if( permitHooks && rc==TH_OK ){ rc = xfer_run_script(zScript, zUuid, 0); } } db_finalize(&q); db_multi_exec("DROP TABLE pending_tkt"); /* If multiple check-ins happen close together in time, adjust their ** times by a few milliseconds to make sure they appear in chronological ** order. |
︙ | ︙ | |||
1520 1521 1522 1523 1524 1525 1526 | db_reset(&q); if( sqlite3_changes(g.db)==0 ) break; db_step(&u); db_reset(&u); } db_finalize(&q); db_finalize(&u); | > | | | < | > > > | 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 | db_reset(&q); if( sqlite3_changes(g.db)==0 ) break; db_step(&u); db_reset(&u); } db_finalize(&q); db_finalize(&u); if( db_exists("SELECT 1 FROM time_fudge") ){ db_multi_exec( "UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid)" " WHERE objid IN (SELECT mid FROM time_fudge);" ); } db_multi_exec("DROP TABLE time_fudge;"); db_end_transaction(0); manifest_crosslink_busy = 0; return ( rc!=TH_ERROR ); } /* ** Make an entry in the event table for a ticket change artifact. */ void manifest_ticket_event( int rid, /* Artifact ID of the change ticket artifact */ |
︙ | ︙ | |||
1555 1556 1557 1558 1559 1560 1561 | blob_zero(&comment); blob_zero(&brief); if( once ){ once = 0; zTitleExpr = db_get("ticket-title-expr", "title"); zStatusColumn = db_get("ticket-status-column", "status"); } | | | | | | | | | | | | | | | | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 | blob_zero(&comment); blob_zero(&brief); if( once ){ once = 0; zTitleExpr = db_get("ticket-title-expr", "title"); zStatusColumn = db_get("ticket-status-column", "status"); } zTitle = db_text("unknown", "SELECT \"%w\" FROM ticket WHERE tkt_uuid=%Q", zTitleExpr, pManifest->zTicketUuid ); if( !isNew ){ for(i=0; i<pManifest->nField; i++){ if( fossil_strcmp(pManifest->aField[i].zName, zStatusColumn)==0 ){ zNewStatus = pManifest->aField[i].zValue; } } if( zNewStatus ){ blob_appendf(&comment, "%h ticket [%!S|%S]: <i>%h</i>", zNewStatus, pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle ); if( pManifest->nField>1 ){ blob_appendf(&comment, " plus %d other change%s", pManifest->nField-1, pManifest->nField==2 ? "" : "s"); } blob_appendf(&brief, "%h ticket [%!S|%S].", zNewStatus, pManifest->zTicketUuid, pManifest->zTicketUuid); }else{ zNewStatus = db_text("unknown", "SELECT \"%w\" FROM ticket WHERE tkt_uuid=%Q", zStatusColumn, pManifest->zTicketUuid ); blob_appendf(&comment, "Ticket [%!S|%S] <i>%h</i> status still %h with " "%d other change%s", pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle, zNewStatus, pManifest->nField, pManifest->nField==1 ? "" : "s" ); fossil_free(zNewStatus); blob_appendf(&brief, "Ticket [%!S|%S]: %d change%s", pManifest->zTicketUuid, pManifest->zTicketUuid, pManifest->nField, pManifest->nField==1 ? "" : "s" ); } }else{ blob_appendf(&comment, "New ticket [%!S|%S] <i>%h</i>.", pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle ); blob_appendf(&brief, "New ticket [%!S|%S].", pManifest->zTicketUuid, pManifest->zTicketUuid); } fossil_free(zTitle); db_multi_exec( "REPLACE INTO event(type,tagid,mtime,objid,user,comment,brief)" "VALUES('t',%d,%.17g,%d,%Q,%Q,%Q)", tktTagId, pManifest->rDate, rid, pManifest->zUser, blob_str(&comment), blob_str(&brief) ); blob_reset(&comment); blob_reset(&brief); } /* ** Add an extra line of text to the end of a manifest to prevent it being ** recognized as a valid manifest. ** ** This routine is called prior to writing out the text of a manifest as ** the "manifest" file in the root of a repository when ** "fossil setting manifest on" is enabled. That way, if the files of ** the project are imported into a different Fossil project, the manifest ** file will not be interpreted as a control artifact in that other project. ** ** Normally it is sufficient to simply append the extra line of text. ** However, if the manifest is PGP signed then the extra line has to be ** inserted before the PGP signature (thus invalidating the signature). */ void sterilize_manifest(Blob *p){ char *z, *zOrig; int n, nOrig; static const char zExtraLine[] = "# Remove this line to create a well-formed manifest.\n"; z = zOrig = blob_materialize(p); n = nOrig = blob_size(p); remove_pgp_signature(&z, &n); if( z==zOrig ){ blob_append(p, zExtraLine, -1); }else{ int iEnd; Blob copy; memcpy(©, p, sizeof(copy)); blob_init(p, 0, 0); iEnd = (int)(&z[n] - zOrig); blob_append(p, zOrig, iEnd); blob_append(p, zExtraLine, -1); blob_append(p, &zOrig[iEnd], -1); blob_zero(©); } } /* ** This is the comparison function used to sort the tag array. */ static int tag_compare(const void *a, const void *b){ struct TagType *pA = (struct TagType*)a; struct TagType *pB = (struct TagType*)b; |
︙ | ︙ | |||
1638 1639 1640 1641 1642 1643 1644 | ** * Attachment ** * Event ** ** If the input is a control artifact, then make appropriate entries ** in the auxiliary tables of the database in order to crosslink the ** artifact. ** | | | | > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > > > > > > > > > > > > | > | | | > | | | 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 | ** * Attachment ** * Event ** ** If the input is a control artifact, then make appropriate entries ** in the auxiliary tables of the database in order to crosslink the ** artifact. ** ** If global variable g.xlinkClusterOnly is true, then ignore all ** control artifacts other than clusters. ** ** This routine always resets the pContent blob before returning. ** ** Historical note: This routine original processed manifests only. ** Processing for other control artifacts was added later. The name ** of the routine, "manifest_crosslink", and the name of this source ** file, is a legacy of its original use. */ int manifest_crosslink(int rid, Blob *pContent, int flags){ int i, rc = TH_OK; Manifest *p; Stmt q; int parentid = 0; int permitHooks = (flags & MC_PERMIT_HOOKS); const char *zScript = 0; const char *zUuid = 0; if( (p = manifest_cache_find(rid))!=0 ){ blob_reset(pContent); }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ assert( blob_is_reset(pContent) || pContent==0 ); if( (flags & MC_NO_ERRORS)==0 ){ fossil_error(1, "syntax error in manifest [%S]", db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid)); } return 0; } if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ manifest_destroy(p); assert( blob_is_reset(pContent) ); if( (flags & MC_NO_ERRORS)==0 ) fossil_error(1, "no manifest"); return 0; } if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ manifest_destroy(p); assert( blob_is_reset(pContent) ); if( (flags & MC_NO_ERRORS)==0 ){ fossil_error(1, "cannot fetch baseline for manifest [%S]", db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid)); } return 0; } db_begin_transaction(); if( p->type==CFTYPE_MANIFEST ){ if( permitHooks ){ zScript = xfer_commit_code(); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); } if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ char *zCom; char zBaseId[30]; if( p->zBaseline ){ sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d", uuid_to_rid(p->zBaseline,1)); }else{ sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL"); } for(i=0; i<p->nParent; i++){ int pid = uuid_to_rid(p->azParent[i], 1); db_multi_exec( "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)" "VALUES(%d, %d, %d, %.17g, %s)", pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/); add_mlink(pid, 0, rid, p, i==0); if( i==0 ) parentid = pid; } if( p->nParent>1 ){ /* Remove incorrect MLINK create-file entries that arise when a ** file is added by merge. */ db_multi_exec( "DELETE FROM mlink" " WHERE mid=%d" " AND pid=0" " AND fnid IN " " (SELECT fnid FROM mlink WHERE mid=%d GROUP BY fnid" " HAVING count(*)<%d)", rid, rid, p->nParent ); } db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid); while( db_step(&q)==SQLITE_ROW ){ int cid = db_column_int(&q, 0); int isprim = db_column_int(&q, 1); add_mlink(rid, p, cid, 0, isprim); } db_finalize(&q); if( p->nParent==0 ){ /* For root files (files without parents) add mlink entries ** showing all content as new. */ int isPublic = !content_is_private(rid); for(i=0; i<p->nFile; i++){ add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0, isPublic, 1, manifest_file_mperm(&p->aFile[i])); } } search_doc_touch('c', rid, 0); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment," "bgcolor,euser,ecomment,omtime)" "VALUES('ci'," " coalesce(" " (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d)," " %.17g" " )," " %d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>0)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d),%.17g);", TAG_DATE, rid, p->rDate, rid, p->zUser, p->zComment, TAG_BGCOLOR, rid, TAG_USER, rid, TAG_COMMENT, rid, p->rDate ); zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event" " WHERE rowid=last_insert_rowid()"); wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE); fossil_free(zCom); /* If this is a delta-manifest, record the fact that this repository ** contains delta manifests, to free the "commit" logic to generate ** new delta manifests. */ if( p->zBaseline!=0 ){ static int once = 1; |
︙ | ︙ | |||
1766 1767 1768 1769 1770 1771 1772 | } if( tid ){ switch( p->aTag[i].zName[0] ){ case '-': type = 0; break; /* Cancel prior occurrences */ case '+': type = 1; break; /* Apply to target only */ case '*': type = 2; break; /* Propagate to descendants */ default: | | | | > | < | | | 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 | } if( tid ){ switch( p->aTag[i].zName[0] ){ case '-': type = 0; break; /* Cancel prior occurrences */ case '+': type = 1; break; /* Apply to target only */ case '*': type = 2; break; /* Propagate to descendants */ default: fossil_error(1, "unknown tag type in manifest: %s", p->aTag); return 0; } tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, rid, p->rDate, tid); } } if( parentid ){ tag_propagate_all(parentid); } } if( p->type==CFTYPE_WIKI ){ char *zTag = mprintf("wiki-%s", p->zWikiTitle); int tagid = tag_findid(zTag, 1); int prior; char *zComment; int nWiki; char zLength[40]; while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; nWiki = strlen(p->zWiki); sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); tag_insert(zTag, 1, zLength, rid, p->rDate, rid); fossil_free(zTag); prior = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=%d AND mtime<%.17g" " ORDER BY mtime DESC", tagid, p->rDate ); if( prior ){ content_deltify(prior, rid, 0); } if( nWiki>0 ){ zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle); }else{ zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle); } search_doc_touch('w',rid,p->zWikiTitle); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment," " bgcolor,euser,ecomment)" "VALUES('w',%.17g,%d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d)," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", p->rDate, rid, p->zUser, zComment, TAG_BGCOLOR, rid, TAG_USER, rid, TAG_COMMENT, rid ); fossil_free(zComment); } if( p->type==CFTYPE_EVENT ){ char *zTag = mprintf("event-%s", p->zEventId); int tagid = tag_findid(zTag, 1); int prior, subsequent; int nWiki; char zLength[40]; while( fossil_isspace(p->zWiki[0]) ) p->zWiki++; nWiki = strlen(p->zWiki); sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki); tag_insert(zTag, 1, zLength, rid, p->rDate, rid); fossil_free(zTag); prior = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=%d AND mtime<%.17g AND rid!=%d" " ORDER BY mtime DESC", tagid, p->rDate, rid ); subsequent = db_int(0, |
︙ | ︙ | |||
1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 | tagid, tagid ); } } if( subsequent ){ content_deltify(rid, subsequent, 0); }else{ db_multi_exec( "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)" "VALUES('e',%.17g,%d,%d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", | > | < | > > > > > | | < | < < | < < < < < < < | | | | | > | | | | | < | | | | | | > > > > > > | | | 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 | tagid, tagid ); } } if( subsequent ){ content_deltify(rid, subsequent, 0); }else{ search_doc_touch('e',rid,0); db_multi_exec( "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)" "VALUES('e',%.17g,%d,%d,%Q,%Q," " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));", p->rEventDate, rid, tagid, p->zUser, p->zComment, TAG_BGCOLOR, rid ); } } if( p->type==CFTYPE_TICKET ){ char *zTag; assert( manifest_crosslink_busy==1 ); zTag = mprintf("tkt-%s", p->zTicketUuid); tag_insert(zTag, 1, 0, rid, p->rDate, rid); fossil_free(zTag); db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", p->zTicketUuid); } if( p->type==CFTYPE_ATTACHMENT ){ char *zComment = 0; const char isAdd = (p->zAttachSrc && p->zAttachSrc[0]) ? 1 : 0; const char attachToType = fossil_is_uuid(p->zAttachTarget) ? 't' /* attach to ticket */ : 'w' /* attach to wiki page */; db_multi_exec( "INSERT INTO attachment(attachid, mtime, src, target," "filename, comment, user)" "VALUES(%d,%.17g,%Q,%Q,%Q,%Q,%Q);", rid, p->rDate, p->zAttachSrc, p->zAttachTarget, p->zAttachName, (p->zComment ? p->zComment : ""), p->zUser ); db_multi_exec( "UPDATE attachment SET isLatest = (mtime==" "(SELECT max(mtime) FROM attachment" " WHERE target=%Q AND filename=%Q))" " WHERE target=%Q AND filename=%Q", p->zAttachTarget, p->zAttachName, p->zAttachTarget, p->zAttachName ); if( 'w' == attachToType ){ if( isAdd ){ zComment = mprintf( "Add attachment [/artifact/%!S|%h] to wiki page [%h]", p->zAttachSrc, p->zAttachName, p->zAttachTarget); }else{ zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]", p->zAttachName, p->zAttachTarget); } }else{ if( isAdd ){ zComment = mprintf( "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]", p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget); }else{ zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]", p->zAttachName, p->zAttachTarget, p->zAttachTarget); } } db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('%c',%.17g,%d,%Q,%Q)", attachToType, p->rDate, rid, p->zUser, zComment ); fossil_free(zComment); } if( p->type==CFTYPE_CONTROL ){ Blob comment; int i; const char *zName; const char *zValue; const char *zTagUuid; int branchMove = 0; blob_zero(&comment); if( p->zComment ){ blob_appendf(&comment, " %s.", p->zComment); } /* Next loop expects tags to be sorted on UUID, so sort it. */ qsort(p->aTag, p->nTag, sizeof(p->aTag[0]), tag_compare); for(i=0; i<p->nTag; i++){ zTagUuid = p->aTag[i].zUuid; if( !zTagUuid ) continue; if( i==0 || fossil_strcmp(zTagUuid, p->aTag[i-1].zUuid)!=0 ){ blob_appendf(&comment, " Edit [%!S|%S]:", zTagUuid, zTagUuid); branchMove = 0; if( permitHooks && db_exists("SELECT 1 FROM event, blob" " WHERE event.type='ci' AND event.objid=blob.rid" " AND blob.uuid=%Q", zTagUuid) ){ zScript = xfer_commit_code(); zUuid = zTagUuid; } } zName = p->aTag[i].zName; zValue = p->aTag[i].zValue; if( strcmp(zName, "*branch")==0 ){ blob_appendf(&comment, " Move to branch [/timeline?r=%h&nd&dp=%!S&unhide | %h].", zValue, zTagUuid, zValue); branchMove = 1; continue; }else if( strcmp(zName, "*bgcolor")==0 ){ blob_appendf(&comment, " Change branch background color to \"%h\".", zValue); continue; }else if( strcmp(zName, "+bgcolor")==0 ){ |
︙ | ︙ | |||
2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 | "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('g',%.17g,%d,%Q,%Q)", p->rDate, rid, p->zUser, blob_str(&comment)+1 ); blob_reset(&comment); } db_end_transaction(0); if( p->type==CFTYPE_MANIFEST ){ manifest_cache_insert(p); }else{ manifest_destroy(p); } assert( blob_is_reset(pContent) ); | > > > > > > | | | 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 | "REPLACE INTO event(type,mtime,objid,user,comment)" "VALUES('g',%.17g,%d,%Q,%Q)", p->rDate, rid, p->zUser, blob_str(&comment)+1 ); blob_reset(&comment); } db_end_transaction(0); if( permitHooks ){ rc = xfer_run_common_script(); if( rc==TH_OK ){ rc = xfer_run_script(zScript, zUuid, 0); } } if( p->type==CFTYPE_MANIFEST ){ manifest_cache_insert(p); }else{ manifest_destroy(p); } assert( blob_is_reset(pContent) ); return ( rc!=TH_ERROR ); } /* ** COMMAND: test-crosslink ** ** Usage: %fossil test-crosslink RECORDID ** ** Run the manifest_crosslink() routine on the artifact with the given ** record ID. This is typically done in the debugger. */ void test_crosslink_cmd(void){ int rid; Blob content; db_find_and_open_repository(0, 0); if( g.argc!=3 ) usage("RECORDID"); rid = name_to_rid(g.argv[2]); content_get(rid, &content); manifest_crosslink(rid, &content, MC_NONE); } |
Changes to src/markdown.c.
︙ | ︙ | |||
159 160 161 162 163 164 165 | int work_active; struct Blob *work; }; /* html_tag -- structure for quick HTML tag search (inspired from discount) */ struct html_tag { | | | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | int work_active; struct Blob *work; }; /* html_tag -- structure for quick HTML tag search (inspired from discount) */ struct html_tag { const char *text; int size; }; /******************** * GLOBAL VARIABLES * ********************/ /* block_tags -- recognised block tags, sorted by cmp_html_tag */ static const struct html_tag block_tags[] = { { "p", 1 }, { "dl", 2 }, { "h1", 2 }, { "h2", 2 }, { "h3", 2 }, { "h4", 2 }, { "h5", 2 }, |
︙ | ︙ | |||
274 275 276 277 278 279 280 | const struct html_tag *htb = b; if( hta->size!=htb->size ) return hta->size-htb->size; return fossil_strnicmp(hta->text, htb->text, hta->size); } /* find_block_tag -- returns the current block tag */ | | | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | const struct html_tag *htb = b; if( hta->size!=htb->size ) return hta->size-htb->size; return fossil_strnicmp(hta->text, htb->text, hta->size); } /* find_block_tag -- returns the current block tag */ static const struct html_tag *find_block_tag(const char *data, size_t size){ size_t i = 0; struct html_tag key; /* looking for the word end */ while( i<size && ((data[i]>='0' && data[i]<='9') || (data[i]>='A' && data[i]<='Z') |
︙ | ︙ | |||
388 389 390 391 392 393 394 | j = i; while( i<size && data[i]!='>' && data[i]!='\'' && data[i]!='"' && data[i]!=' ' && data[i]!='\t' | | | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | j = i; while( i<size && data[i]!='>' && data[i]!='\'' && data[i]!='"' && data[i]!=' ' && data[i]!='\t' && data[i]!='\n' ){ i++; } if( i>=size ) return 0; if( i>j && data[i]=='>' ) return i+1; /* one of the forbidden chars has been found */ *autolink = MKDA_NOT_AUTOLINK; |
︙ | ︙ | |||
840 841 842 843 844 845 846 | return 0; }else{ return end; } } | | > > | 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 | return 0; }else{ return end; } } /* get_link_inline -- extract inline-style link and title from ** parenthesed data */ static int get_link_inline( struct Blob *link, struct Blob *title, char *data, size_t size ){ size_t i = 0, mark; |
︙ | ︙ | |||
1068 1069 1070 1071 1072 1073 1074 | /********************************* * BLOCK-LEVEL PARSING FUNCTIONS * *********************************/ /* is_empty -- returns the line length when it is empty, 0 otherwise */ | | | 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 | /********************************* * BLOCK-LEVEL PARSING FUNCTIONS * *********************************/ /* is_empty -- returns the line length when it is empty, 0 otherwise */ static size_t is_empty(const char *data, size_t size){ size_t i; for(i=0; i<size && data[i]!='\n'; i++){ if( data[i]!=' ' && data[i]!='\t' ) return 0; } return i+1; } |
︙ | ︙ | |||
1520 1521 1522 1523 1524 1525 1526 | /* non-recursive fallback when working buffer stack is full */ if( !inter ){ if( rndr->make.listitem ){ rndr->make.listitem(ob, work, *flags, rndr->make.opaque); } if( work!=&fallback ) release_work_buffer(rndr, work); | | | 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 | /* non-recursive fallback when working buffer stack is full */ if( !inter ){ if( rndr->make.listitem ){ rndr->make.listitem(ob, work, *flags, rndr->make.opaque); } if( work!=&fallback ) release_work_buffer(rndr, work); blob_reset(&fallback); return beg; } /* render of li contents */ if( has_inside_empty ) *flags |= MKD_LI_BLOCK; if( *flags & MKD_LI_BLOCK ){ /* intermediate render of block li */ |
︙ | ︙ | |||
1556 1557 1558 1559 1560 1561 1562 | /* render of li itself */ if( rndr->make.listitem ){ rndr->make.listitem(ob, inter, *flags, rndr->make.opaque); } release_work_buffer(rndr, inter); if( work!=&fallback ) release_work_buffer(rndr, work); | | | 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 | /* render of li itself */ if( rndr->make.listitem ){ rndr->make.listitem(ob, inter, *flags, rndr->make.opaque); } release_work_buffer(rndr, inter); if( work!=&fallback ) release_work_buffer(rndr, work); blob_reset(&fallback); return beg; } /* parse_list -- parsing ordered or unordered list block */ static size_t parse_list( struct Blob *ob, |
︙ | ︙ | |||
1582 1583 1584 1585 1586 1587 1588 | j = parse_listitem(work, rndr, data+i, size-i, &flags); i += j; if( !j || (flags & MKD_LI_END) ) break; } if( rndr->make.list ) rndr->make.list(ob, work, flags, rndr->make.opaque); if( work!=&fallback ) release_work_buffer(rndr, work); | | | 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 | j = parse_listitem(work, rndr, data+i, size-i, &flags); i += j; if( !j || (flags & MKD_LI_END) ) break; } if( rndr->make.list ) rndr->make.list(ob, work, flags, rndr->make.opaque); if( work!=&fallback ) release_work_buffer(rndr, work); blob_reset(&fallback); return i; } /* parse_atxheader -- parsing of atx-style headers */ static size_t parse_atxheader( struct Blob *ob, |
︙ | ︙ | |||
1629 1630 1631 1632 1633 1634 1635 | } return skip; } /* htmlblock_end -- checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */ /* returns the length on match, 0 otherwise */ | | > > > > | 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 | } return skip; } /* htmlblock_end -- checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */ /* returns the length on match, 0 otherwise */ static size_t htmlblock_end( const struct html_tag *tag, const char *data, size_t size ){ size_t i, w; /* assuming data[0]=='<' && data[1]=='/' already tested */ /* checking tag is a match */ if( (tag->size+3)>=size || fossil_strnicmp(data+2, tag->text, tag->size) |
︙ | ︙ | |||
1666 1667 1668 1669 1670 1671 1672 | static size_t parse_htmlblock( struct Blob *ob, struct render *rndr, char *data, size_t size ){ size_t i, j = 0; | | | 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 | static size_t parse_htmlblock( struct Blob *ob, struct render *rndr, char *data, size_t size ){ size_t i, j = 0; const struct html_tag *curtag; int found; size_t work_size = 0; struct Blob work = BLOB_INITIALIZER; /* identification of the opening tag */ if( size<2 || data[0]!='<' ) return 0; curtag = find_block_tag(data+1, size-1); |
︙ | ︙ | |||
2153 2154 2155 2156 2157 2158 2159 | void markdown( struct Blob *ob, /* output blob for rendered text */ struct Blob *ib, /* input blob in markdown */ const struct mkd_renderer *rndrer /* renderer descriptor (callbacks) */ ){ struct link_ref *lr; struct Blob text = BLOB_INITIALIZER; | | | 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 | void markdown( struct Blob *ob, /* output blob for rendered text */ struct Blob *ib, /* input blob in markdown */ const struct mkd_renderer *rndrer /* renderer descriptor (callbacks) */ ){ struct link_ref *lr; struct Blob text = BLOB_INITIALIZER; size_t i, beg, end = 0; struct render rndr; char *ib_data; /* filling the render structure */ if( !rndrer ) return; rndr.make = *rndrer; if( rndr.make.max_work_stack<1 ) rndr.make.max_work_stack = 1; |
︙ | ︙ | |||
2222 2223 2224 2225 2226 2227 2228 | /* second pass: actual rendering */ if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); if( rndr.make.epilog ) rndr.make.epilog(ob, rndr.make.opaque); /* clean-up */ | | | | | | | 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 | /* second pass: actual rendering */ if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); if( rndr.make.epilog ) rndr.make.epilog(ob, rndr.make.opaque); /* clean-up */ blob_reset(&text); lr = (struct link_ref *)blob_buffer(&rndr.refs); end = blob_size(&rndr.refs)/sizeof(struct link_ref); for(i=0; i<end; i++){ blob_reset(&lr[i].id); blob_reset(&lr[i].link); blob_reset(&lr[i].title); } blob_reset(&rndr.refs); blobarray_zero(rndr.work, rndr.make.max_work_stack); fossil_free(rndr.work); } |
Added src/markdown.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | # Markdown formatting rules In addition to its native Wiki formatting syntax, Fossil supports Markdown syntax as specified by [John Gruber's original Markdown implementation](http://daringfireball.net/projects/markdown/). For lots of examples - not repeated here - please refer to its [syntax description](http://daringfireball.net/projects/markdown/syntax), of which the page you are reading is an extract. This page itself uses Markdown formatting. ## Summary - Block elements * A **paragraph** is a group of consecutive lines. Paragraphs are separated by blank lines. * A **Header** is a line of text underlined with equal signs or hyphens, or prefixed by a number of hash marks. * **Block quotes** are blocks of text prefixed by '>'. * **Ordered list** items are prefixed by a number and a period. **Unordered list** items are prefixed by a hyphen, asterisk or plus sign. Prefix and item text are separated by whitespace. * **Code blocks** are formed by lines of text (possibly including empty lines) prefixed by at least 4 spaces or a tab. * A **horizontal rule** is a line consisting of 3 or more asterisks, hyphens or underscores, with optional whitespace between them. - Span elements * 3 types of **links** exist: - **automatic links** are URLs or email addresses enclosed in angle brackets ('<' and '>'), and are displayed as such. - **inline links** consist of the displayed link text in square brackets ('[' and ']'), followed by the link target in parentheses. - **reference links** separate _link instance_ from _link definition_. A link instance consists of the displayed link text in square brackets, followed by a link definition name in square brackets. The corresponding link definition can occur anywhere on the page, and consists of the link definition name in square brackets followed by a colon, whitespace and the link target. * **Emphasis** can be given by wrapping text in one or two asterisks or underscores - use one for HTML `<em>`, and two for `<strong>` emphasis. * A **code span** is text wrapped in backticks ('`'). * **Images** use a syntax much like inline or reference links, but with alt attribute text ('img alt=...') instead of link text, and the first pair of square brackets in an image instance prefixed by an exclamation mark. - **Inline HTML** is mostly interpreted automatically. - **Escaping** Markdown punctuation characters is done by prefixing them by a backslash ('\\'). ## Details ### Paragraphs To cause an explicit line break within a paragraph, use 2 or more spaces at the end of a line. Any line containing only whitespace (space or tab characters) is considered a blank line. ### Headers #### 'Setext' style headers (underlined) The number of underlining equal signs or hyphens used has no impact on the resulting header. Underlining using equal sign(s) creates a top level header (corresponding to HTML `<h1>`), while hyphen(s) create a second level header (HTML `<h2>`). Thus, only 2 levels of headers can be made this way. #### 'Atx' style headers (hash prefixed) 1 to 6 hash characters can be used to indicate header levels 1 (HTML `<h1>`) to 6 (`<h6>`). Headers may optionally be 'closed' for cosmetic reasons, by appending a whitespace and hash characters to the header. The number of trailing hash characters has no impact on the header level. ### Block quotes Not every line in a paragraph needs to be prefixed by '>' in order to make it a block quote, only the first line. Block quoted paragraphs can be nested by using multiple '>' characters as prefix. Within a block quote, Markdown formatting (e.g. lists, emphasis) still works as normal. ### Lists A list item prefix need not occur first on its line; up to 3 leading spaces are allowed (4 spaces would make a code block out of the following text). For unordered lists, asterisks, hyphens and plus signs can be used interchangeably. For ordered lists, arbitrary numbers can be used as part of an item prefix; the items will be renumbered during rendering. However, future implementations may demand that the number used for the first item in a list indicates an offset to be used for subsequent items. For list items spanning multiple lines, subsequent lines can be indented using an arbitrary amount of whitespace. List items will be wrapped in HTML `<p>` tags if they are separated by blank lines. A list item may span multiple paragraphs. At least the first line of each such paragraph must be indented using at least 4 spaces or a tab character. Block quotes within list items must have their '>' delimiters indented using 4 up to 7 spaces. Code blocks within list items need to be indented _twice_, that is, using 8 spaces or 2 tab characters. ### Code blocks Lines within a code block are rendered verbatim using HTML `<pre>` and `<code>` tags, except that HTML punctuation characters like '<' and '&' are automatically converted to HTML entities. Thus, there is no need to explicitly escape HTML syntax within a code block. A code block runs until the first non blank line with indent less than 4 spaces or 1 tab character. Regular Markdown syntax is not processed within code blocks. ### Links #### Automatic links When rendering automatic links to email addresses, HTML encoding obfuscation is used to prevent some spambots from harvesting. #### Inline links Links to resources on the same server can use relative paths (i.e. can start with a '/'). An optional title for the link (e.g. to have mouseover text in the browser) may be given behind the link target but within the parentheses, in single and double quotes, and separated from the link target by whitespace. #### Reference links > Each reference link consists of > > - one or more _link instances_ at appropriate locations in the page text > - a single _link definition_ at an arbitrary location on the page > > During rendering, each link instance is resolved, and the corresponding definition is > filled in. No separate link definition clauses occur in the rendered output. > > There are 3 fields involved in link instances and definitions: > > - link text (i.e. the text that is displayed at the resulting link) > - link definition name (i.e. an unique ID binding link instances to link definition) > - link target (a target URL for the link) Multiple link instances may reference the same link definition using its link definition name. Link definition names are case insensitive, and may contain letters, numbers, spaces and punctuation. ##### Link instance A space may be inserted between the bracket pairs for link text and link definition name. A link instance can use an _implicit link definition name_ shortcut, in which case the link text is used as the link definition name. The second set of brackets then remains empty, e.g. '[Google][]' ('Google' being used as both link text and link definition name). ##### Link definition The first bracket pair containing the link definition name may be indented using up to 3 spaces. The link target may optionally be surrounded by angle brackets ('<' and '>'). A link target may be followed by an optional title (e.g. to have mouseover text in the browser). This title may be enclosed in parentheses, single or double quotes. Link definitions may be split into 2 lines, with the title on the second line, arbitrarily indented. This may be more visually pleasing when using long link targets. ### Emphasis The same character(s) used for starting the emphasis must be used to end it; don't mix asterisks and underscores. Emphasis can be used in the middle of a word. That is, there need not be whitespace on either side of emphasis start or end punctuation characters. ### Code spans To include a literal backtick character in a code span, use multiple backticks as opening and closing delimiters. Whitespace may exist immediately after the opening delimiter and before the closing delimiter of a code span, to allow for code fragments starting or ending with a backtick. Within a code span - like within a code block - angle brackets and ampersands are automatically encoded to make including HTML fragments easier. ### Images If necessary, HTML must be used to specify image dimensions. Markdown has no provision for this. ### Inline HTML Start and end tags of a HTML block level construct (`<div>`, `<table>` etc) must be separated from surrounding context using blank lines, and must both occur at the start of a line. No extra unwanted `<p>` HTML tags are added around HTML block level tags. Markdown formatting within HTML block level tags is not processed; however, formatting within span level tags (e.g. `<mark>`) is processed normally. ### Escaping Markdown punctuation The following punctuation characters can be escaped using backslash: - \\ backslash - ` backtick - * asterisk - _ underscore - {} curly braces - [] square brackets - () parentheses - # hash mark - + plus sign - - minus sign (hyphen) - . dot - ! exclamation mark To render a literal backslash, use 2 backslashes ('\\\\'). |
Changes to src/markdown_html.c.
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | i++; } } } /* HTML block tags */ static void html_raw_block(struct Blob *ob, struct Blob *text, void *opaque){ char *data = blob_buffer(text); size_t first = 0, size = blob_size(text); INTER_BLOCK(ob); while( first<size && data[first]=='\n' ) first++; while( size>first && data[size-1]=='\n' ) size--; | > > > > > > > > > > > > > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | i++; } } } /* HTML block tags */ /* Size of the prolog: "<div class='markdown'>\n" */ #define PROLOG_SIZE 23 static void html_prolog(struct Blob *ob, void *opaque){ INTER_BLOCK(ob); BLOB_APPEND_LITTERAL(ob, "<div class=\"markdown\">\n"); assert( blob_size(ob)==PROLOG_SIZE ); } static void html_epilog(struct Blob *ob, void *opaque){ INTER_BLOCK(ob); BLOB_APPEND_LITTERAL(ob, "</div>\n"); } static void html_raw_block(struct Blob *ob, struct Blob *text, void *opaque){ char *data = blob_buffer(text); size_t first = 0, size = blob_size(text); INTER_BLOCK(ob); while( first<size && data[first]=='\n' ) first++; while( size>first && data[size-1]=='\n' ) size--; |
︙ | ︙ | |||
114 115 116 117 118 119 120 | struct Blob *text, int level, void *opaque ){ struct Blob *title = opaque; /* The first header at the beginning of a text is considered as * a title and not output. */ | | < | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | struct Blob *text, int level, void *opaque ){ struct Blob *title = opaque; /* The first header at the beginning of a text is considered as * a title and not output. */ if( blob_size(ob)<=PROLOG_SIZE && blob_size(title)==0 ){ BLOB_APPEND_BLOB(title, text); } INTER_BLOCK(ob); blob_appendf(ob, "<h%d>", level); BLOB_APPEND_BLOB(ob, text); blob_appendf(ob, "</h%d>", level); } |
︙ | ︙ | |||
362 363 364 365 366 367 368 | void markdown_to_html( struct Blob *input_markdown, struct Blob *output_title, struct Blob *output_body ){ struct mkd_renderer html_renderer = { | | > > | 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 | void markdown_to_html( struct Blob *input_markdown, struct Blob *output_title, struct Blob *output_body ){ struct mkd_renderer html_renderer = { /* prolog and epilog */ html_prolog, html_epilog, /* block level elements */ html_blockcode, html_blockquote, html_raw_block, html_header, html_hrule, |
︙ | ︙ | |||
395 396 397 398 399 400 401 | /* low level elements */ 0, /* entities are copied verbatim */ html_normal_text, /* misc. parameters */ 64, /* maximum stack */ "*_", /* emphasis characters */ | | > | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | /* low level elements */ 0, /* entities are copied verbatim */ html_normal_text, /* misc. parameters */ 64, /* maximum stack */ "*_", /* emphasis characters */ 0 /* opaque data */ }; html_renderer.opaque = output_title; blob_reset(output_title); blob_reset(output_body); markdown(output_body, input_markdown, &html_renderer); } |
Changes to src/md5.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ #include <string.h> #include <stdio.h> #include <sqlite3.h> #include "md5.h" /* * If compiled on a machine that doesn't have a 32-bit integer, | > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ #include "config.h" #include <string.h> #include <stdio.h> #include <sqlite3.h> #include "md5.h" /* * If compiled on a machine that doesn't have a 32-bit integer, |
︙ | ︙ | |||
161 162 163 164 165 166 167 | } /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ static void MD5Init(MD5Context *ctx){ | | | | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | } /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ static void MD5Init(MD5Context *ctx){ ctx->isInit = 1; ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ static void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){ struct Context *ctx = (struct Context *)pCtx; uint32 t; /* Update bitcount */ t = ctx->bits[0]; |
︙ | ︙ | |||
221 222 223 224 225 226 227 | /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* | | | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ static void MD5Final(unsigned char digest[16], MD5Context *pCtx){ struct Context *ctx = (struct Context *)pCtx; unsigned count; unsigned char *p; |
︙ | ︙ | |||
271 272 273 274 275 276 277 | /* ** Convert a digest into base-16. digest should be declared as ** "unsigned char digest[16]" in the calling function. The MD5 ** digest is stored in the first 16 bytes. zBuf should ** be "char zBuf[33]". */ static void DigestToBase16(unsigned char *digest, char *zBuf){ | | | 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | /* ** Convert a digest into base-16. digest should be declared as ** "unsigned char digest[16]" in the calling function. The MD5 ** digest is stored in the first 16 bytes. zBuf should ** be "char zBuf[33]". */ static void DigestToBase16(unsigned char *digest, char *zBuf){ static const char zEncode[] = "0123456789abcdef"; int i, j; for(j=i=0; i<16; i++){ int a = digest[i]; zBuf[j++] = zEncode[(a>>4)&0xf]; zBuf[j++] = zEncode[a & 0xf]; } |
︙ | ︙ | |||
340 341 342 343 344 345 346 | } sqlite3_snprintf(sizeof(zResult), zResult, "%08x", cksum); return zResult; } /* ** Finish the incremental MD5 checksum. Store the result in blob pOut | | | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | } sqlite3_snprintf(sizeof(zResult), zResult, "%08x", cksum); return zResult; } /* ** Finish the incremental MD5 checksum. Store the result in blob pOut ** if pOut!=0. Also return a pointer to the result. ** ** This resets the incremental checksum preparing for the next round ** of computation. The return pointer points to a static buffer that ** is overwritten by subsequent calls to this function. */ char *md5sum_finish(Blob *pOut){ unsigned char zResult[16]; |
︙ | ︙ | |||
428 429 430 431 432 433 434 | ** Compute an MD5 checksum of all files named on the command-line. ** If a file is named "-" then content is read from standard input. */ void md5sum_test(void){ int i; Blob in; Blob cksum; | | | 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | ** Compute an MD5 checksum of all files named on the command-line. ** If a file is named "-" then content is read from standard input. */ void md5sum_test(void){ int i; Blob in; Blob cksum; for(i=2; i<g.argc; i++){ blob_init(&cksum, "********** not found ***********", -1); if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ blob_read_from_channel(&in, stdin, -1); md5sum_blob(&in, &cksum); }else{ md5sum_file(g.argv[i], &cksum); } fossil_print("%s %s\n", blob_str(&cksum), g.argv[i]); blob_reset(&cksum); } } |
Changes to src/merge.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 | /* ** Print information about a particular check-in. */ void print_checkin_description(int rid, int indent, const char *zLabel){ Stmt q; db_prepare(&q, | | | | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | /* ** Print information about a particular check-in. */ void print_checkin_description(int rid, int indent, const char *zLabel){ Stmt q; db_prepare(&q, "SELECT datetime(mtime%s)," " coalesce(euser,user), coalesce(ecomment,comment)," " (SELECT uuid FROM blob WHERE rid=%d)," " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref" " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" " AND tagxref.rid=%d AND tagxref.tagtype>0)" " FROM event WHERE objid=%d", timeline_utc(), rid, rid, rid); if( db_step(&q)==SQLITE_ROW ){ const char *zTagList = db_column_text(&q, 4); char *zCom; if( zTagList && zTagList[0] ){ zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList); }else{ zCom = mprintf("%s", db_column_text(&q,2)); } fossil_print("%-*s [%S] by %s on %s\n%*s", indent-1, zLabel, db_column_text(&q, 3), db_column_text(&q, 1), db_column_text(&q, 0), indent, ""); comment_print(zCom, db_column_text(&q,2), indent, -1, g.comFmtFlags); fossil_free(zCom); } db_finalize(&q); } /* |
︙ | ︙ | |||
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | ** ** --case-sensitive BOOL Override the case-sensitive setting. If false, ** files whose names differ only in case are taken ** to be the same file. ** ** -f|--force Force the merge even if it would be a no-op. ** ** --integrate Merged branch will be closed when committing. ** ** -n|--dry-run If given, display instead of run actions ** ** -v|--verbose Show additional details of the merge */ void merge_cmd(void){ int vid; /* Current version "V" */ int mid; /* Version we are merging from "M" */ int pid; /* The pivot version - most recent common ancestor P */ int verboseFlag; /* True if the -v|--verbose option is present */ int integrateFlag; /* True if the --integrate option is present */ int pickFlag; /* True if the --cherrypick option is present */ int backoutFlag; /* True if the --backout option is present */ int dryRunFlag; /* True if the --dry-run or -n option is present */ int forceFlag; /* True if the --force or -f option is present */ const char *zBinGlob; /* The value of --binary */ const char *zPivot; /* The value of --baseline */ int debugFlag; /* True if --debug is present */ int nChng; /* Number of file name changes */ int *aChng; /* An array of file name changes */ int i; /* Loop counter */ int nConflict = 0; /* Number of conflicts seen */ int nOverwrite = 0; /* Number of unmanaged files overwritten */ Stmt q; /* Notation: ** ** V The current checkout ** M The version being merged in ** P The "pivot" - the most recent common ancestor of V and M. */ undo_capture_command_line(); verboseFlag = find_option("verbose","v",0)!=0; if( !verboseFlag ){ verboseFlag = find_option("detail",0,0)!=0; /* deprecated */ } pickFlag = find_option("cherrypick",0,0)!=0; integrateFlag = find_option("integrate",0,0)!=0; backoutFlag = find_option("backout",0,0)!=0; debugFlag = find_option("debug",0,0)!=0; zBinGlob = find_option("binary",0,1); dryRunFlag = find_option("dry-run","n",0)!=0; if( !dryRunFlag ){ dryRunFlag = find_option("nochange",0,0)!=0; /* deprecated */ } forceFlag = find_option("force","f",0)!=0; zPivot = find_option("baseline",0,1); | > > > > < | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | ** ** --case-sensitive BOOL Override the case-sensitive setting. If false, ** files whose names differ only in case are taken ** to be the same file. ** ** -f|--force Force the merge even if it would be a no-op. ** ** --force-missing Force the merge even if there is missing content. ** ** --integrate Merged branch will be closed when committing. ** ** -n|--dry-run If given, display instead of run actions ** ** -v|--verbose Show additional details of the merge */ void merge_cmd(void){ int vid; /* Current version "V" */ int mid; /* Version we are merging from "M" */ int pid; /* The pivot version - most recent common ancestor P */ int verboseFlag; /* True if the -v|--verbose option is present */ int integrateFlag; /* True if the --integrate option is present */ int pickFlag; /* True if the --cherrypick option is present */ int backoutFlag; /* True if the --backout option is present */ int dryRunFlag; /* True if the --dry-run or -n option is present */ int forceFlag; /* True if the --force or -f option is present */ int forceMissingFlag; /* True if the --force-missing option is present */ const char *zBinGlob; /* The value of --binary */ const char *zPivot; /* The value of --baseline */ int debugFlag; /* True if --debug is present */ int nChng; /* Number of file name changes */ int *aChng; /* An array of file name changes */ int i; /* Loop counter */ int nConflict = 0; /* Number of conflicts seen */ int nOverwrite = 0; /* Number of unmanaged files overwritten */ Stmt q; /* Notation: ** ** V The current checkout ** M The version being merged in ** P The "pivot" - the most recent common ancestor of V and M. */ undo_capture_command_line(); verboseFlag = find_option("verbose","v",0)!=0; forceMissingFlag = find_option("force-missing",0,0)!=0; if( !verboseFlag ){ verboseFlag = find_option("detail",0,0)!=0; /* deprecated */ } pickFlag = find_option("cherrypick",0,0)!=0; integrateFlag = find_option("integrate",0,0)!=0; backoutFlag = find_option("backout",0,0)!=0; debugFlag = find_option("debug",0,0)!=0; zBinGlob = find_option("binary",0,1); dryRunFlag = find_option("dry-run","n",0)!=0; if( !dryRunFlag ){ dryRunFlag = find_option("nochange",0,0)!=0; /* deprecated */ } forceFlag = find_option("force","f",0)!=0; zPivot = find_option("baseline",0,1); verify_all_options(); db_must_be_within_tree(); if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("nothing is checked out"); } |
︙ | ︙ | |||
161 162 163 164 165 166 167 | fossil_fatal("not a version: %s", g.argv[2]); } }else if( g.argc==2 ){ /* No version specified on the command-line so pick the most recent ** leaf that is (1) not the version currently checked out and (2) ** has not already been merged into the current checkout and (3) ** the leaf is not closed and (4) the leaf is in the same branch | | | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | fossil_fatal("not a version: %s", g.argv[2]); } }else if( g.argc==2 ){ /* No version specified on the command-line so pick the most recent ** leaf that is (1) not the version currently checked out and (2) ** has not already been merged into the current checkout and (3) ** the leaf is not closed and (4) the leaf is in the same branch ** as the current checkout. */ Stmt q; if( pickFlag || backoutFlag || integrateFlag){ fossil_fatal("cannot use --backout, --cherrypick or --integrate with a fork merge"); } mid = db_int(0, "SELECT leaf.rid" |
︙ | ︙ | |||
193 194 195 196 197 198 199 | db_text(0, "SELECT value FROM tagxref" " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_BRANCH, vid) ); } db_prepare(&q, "SELECT blob.uuid," | | | | | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | db_text(0, "SELECT value FROM tagxref" " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_BRANCH, vid) ); } db_prepare(&q, "SELECT blob.uuid," " datetime(event.mtime%s)," " coalesce(ecomment, comment)," " coalesce(euser, user)" " FROM event, blob" " WHERE event.objid=%d AND blob.rid=%d", timeline_utc(), mid, mid ); if( db_step(&q)==SQLITE_ROW ){ char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"", db_column_text(&q, 0), db_column_text(&q, 1), db_column_text(&q, 3), db_column_text(&q, 2)); comment_print(zCom, db_column_text(&q,2), 0, -1, g.comFmtFlags); fossil_free(zCom); } db_finalize(&q); }else{ usage("?OPTIONS? ?VERSION?"); return; } |
︙ | ︙ | |||
256 257 258 259 260 261 262 | fossil_fatal("not a version: record #%d", pid); } if( !forceFlag && mid==pid ){ fossil_print("Merge skipped because it is a no-op. " " Use --force to override.\n"); return; } | | < < < < < < < | | < | > > | > > | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | fossil_fatal("not a version: record #%d", pid); } if( !forceFlag && mid==pid ){ fossil_print("Merge skipped because it is a no-op. " " Use --force to override.\n"); return; } if( integrateFlag && !is_a_leaf(mid)){ fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]); integrateFlag = 0; } if( verboseFlag ){ print_checkin_description(mid, 12, integrateFlag?"integrate:":"merge-from:"); print_checkin_description(pid, 12, "baseline:"); } vfile_check_signature(vid, CKSIG_ENOTFILE); db_begin_transaction(); if( !dryRunFlag ) undo_begin(); if( load_vfile_from_rid(mid) && !forceMissingFlag ){ fossil_fatal("missing content, unable to merge"); } if( load_vfile_from_rid(pid) && !forceMissingFlag ){ fossil_fatal("missing content, unable to merge"); } if( debugFlag ){ char *z; z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", pid); fossil_print("P=%d %z\n", pid, z); z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); fossil_print("M=%d %z\n", mid, z); z = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
︙ | ︙ | |||
471 472 473 474 475 476 477 | fossil_free(zFullName); if( !dryRunFlag ){ undo_save(zName); vfile_to_disk(0, idm, 0, 0); } } db_finalize(&q); | | | | 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 | fossil_free(zFullName); if( !dryRunFlag ){ undo_save(zName); vfile_to_disk(0, idm, 0, 0); } } db_finalize(&q); /* ** Find files that have changed from P->M but not P->V. ** Copy the M content over into V. */ db_prepare(&q, "SELECT idv, ridm, fn, islinkm FROM fv" " WHERE idp>0 AND idv>0 AND idm>0" " AND ridm!=ridp AND ridv=ridp AND NOT chnged" ); |
︙ | ︙ | |||
523 524 525 526 527 528 529 | int islinkv = db_column_int(&q, 7); int islinkm = db_column_int(&q, 8); int rc; char *zFullPath; Blob m, p, r; /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ if( verboseFlag ){ | | | | 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 | int islinkv = db_column_int(&q, 7); int islinkm = db_column_int(&q, 8); int rc; char *zFullPath; Blob m, p, r; /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ if( verboseFlag ){ fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n", zName, ridp, ridm, ridv); }else{ fossil_print("MERGE %s\n", zName); } if( islinkv || islinkm /* || file_wd_islink(zFullPath) */ ){ fossil_print("***** Cannot merge symlink %s\n", zName); nConflict++; }else{ undo_save(zName); zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); content_get(ridp, &p); content_get(ridm, &m); if( isBinary ){ rc = -1; |
︙ | ︙ | |||
642 643 644 645 646 647 648 | } if( nOverwrite ){ fossil_warning("WARNING: %d unmanaged files were overwritten", nOverwrite); } if( dryRunFlag ){ fossil_warning("REMINDER: this was a dry run -" | | | 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 | } if( nOverwrite ){ fossil_warning("WARNING: %d unmanaged files were overwritten", nOverwrite); } if( dryRunFlag ){ fossil_warning("REMINDER: this was a dry run -" " no files were actually changed."); } /* ** Clean up the mid and pid VFILE entries. Then commit the changes. */ db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid); if( pickFlag ){ |
︙ | ︙ |
Changes to src/merge3.c.
︙ | ︙ | |||
134 135 136 137 138 139 140 | } return i; } /* ** Text of boundary markers for merge conflicts. */ | | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | } return i; } /* ** Text of boundary markers for merge conflicts. */ static const char *const mergeMarker[] = { /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", "======= COMMON ANCESTOR content follows ============================\n", "======= MERGED IN content follows ==================================\n", ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" }; |
︙ | ︙ | |||
266 267 268 269 270 271 272 | */ int sz = 1; /* Size of the conflict in lines */ nConflict++; while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ sz++; } DEBUG( printf("CONFLICT %d\n", sz); ) | | | | | | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | */ int sz = 1; /* Size of the conflict in lines */ nConflict++; while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ sz++; } DEBUG( printf("CONFLICT %d\n", sz); ) blob_append(pOut, mergeMarker[0], -1); i1 = output_one_side(pOut, pV1, aC1, i1, sz); blob_append(pOut, mergeMarker[1], -1); blob_copy_lines(pOut, pPivot, sz); blob_append(pOut, mergeMarker[2], -1); i2 = output_one_side(pOut, pV2, aC2, i2, sz); blob_append(pOut, mergeMarker[3], -1); } /* If we are finished with an edit triple, advance to the next ** triple. */ if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3; if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3; |
︙ | ︙ | |||
362 363 364 365 366 367 368 | ** that the "Xbase.c" is an exact copy of the last imported "Xup.c". ** Then to import the latest "Xup.c" while preserving all the local changes: ** ** fossil 3-way-merge Xbase.c Xlocal.c Xup.c Xlocal.c ** cp Xup.c Xbase.c ** # Verify that everything still works ** fossil commit | | > > > > | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | ** that the "Xbase.c" is an exact copy of the last imported "Xup.c". ** Then to import the latest "Xup.c" while preserving all the local changes: ** ** fossil 3-way-merge Xbase.c Xlocal.c Xup.c Xlocal.c ** cp Xup.c Xbase.c ** # Verify that everything still works ** fossil commit ** */ void delta_3waymerge_cmd(void){ Blob pivot, v1, v2, merged; /* We should be done with options.. */ verify_all_options(); if( g.argc!=6 ){ usage("PIVOT V1 V2 MERGED"); } if( blob_read_from_file(&pivot, g.argv[2])<0 ){ fossil_fatal("cannot read %s\n", g.argv[2]); } if( blob_read_from_file(&v1, g.argv[3])<0 ){ |
︙ | ︙ |
Added src/miniz.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 | /* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Change History 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti - Merged MZ_FORCEINLINE fix from hdeanclark - Fix <time.h> include before config #ifdef, thanks emil.brink - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can set it to 1 for real-time compression). - Merged in some compiler fixes from paulharris's github repro. - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect). 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> for the feedback/bug report. 5/28/11 v1.11 - Added statement from unlicense.org 5/27/11 v1.10 - Substantial compressor optimizations: - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. - Refactored the compression code for better readability and maintainability. - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files). 5/15/11 v1.09 - Initial stable release. * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #ifndef MINIZ_HEADER_INCLUDED #define MINIZ_HEADER_INCLUDED #include <stdlib.h> // Defines to completely disable specific portions of miniz.c: // If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. // Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. //#define MINIZ_NO_STDIO // If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or // get/set file times, and the C run-time funcs that get/set times won't be called. // The current downside is the times written to your archives will be from 1979. //#define MINIZ_NO_TIME // Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. //#define MINIZ_NO_ARCHIVE_APIS // Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. //#define MINIZ_NO_ARCHIVE_WRITING_APIS // Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. //#define MINIZ_NO_ZLIB_APIS // Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. //#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES // Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. // Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc // callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user // functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. //#define MINIZ_NO_MALLOC #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux #define MINIZ_NO_TIME #endif #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include <time.h> #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) // MINIZ_X86_OR_X64_CPU is only used to help set the below macros. #define MINIZ_X86_OR_X64_CPU 1 #endif #if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU // Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. #define MINIZ_LITTLE_ENDIAN 1 #endif #if MINIZ_X86_OR_X64_CPU // Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) // Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). #define MINIZ_HAS_64BIT_REGISTERS 1 #endif #ifdef __cplusplus extern "C" { #endif // ------------------- zlib-style API Definitions. // For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! typedef unsigned long mz_ulong; // mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. void mz_free(void *p); #define MZ_ADLER32_INIT (1) // mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) // mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); // Compression strategies. enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; // Method #define MZ_DEFLATED 8 #ifndef MINIZ_NO_ZLIB_APIS // Heap allocation callbacks. // Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); #define MZ_VERSION "9.1.15" #define MZ_VERNUM 0x91F0 #define MZ_VER_MAJOR 9 #define MZ_VER_MINOR 1 #define MZ_VER_REVISION 15 #define MZ_VER_SUBREVISION 0 // Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; // Return status codes. MZ_PARAM_ERROR is non-standard. enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; // Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; // Window bits #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; // Compression/decompression stream struct. typedef struct mz_stream_s { const unsigned char *next_in; // pointer to next byte to read unsigned int avail_in; // number of bytes available at next_in mz_ulong total_in; // total number of bytes consumed so far unsigned char *next_out; // pointer to next byte to write unsigned int avail_out; // number of bytes that can be written to next_out mz_ulong total_out; // total number of bytes produced so far char *msg; // error msg (unused) struct mz_internal_state *state; // internal state, allocated by zalloc/zfree mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) mz_free_func zfree; // optional heap free function (defaults to free) void *opaque; // heap alloc function user pointer int data_type; // data_type (unused) mz_ulong adler; // adler32 of the source or uncompressed data mz_ulong reserved; // not used } mz_stream; typedef mz_stream *mz_streamp; // Returns the version string of miniz.c. const char *mz_version(void); // mz_deflateInit() initializes a compressor with default options: // Parameters: // pStream must point to an initialized mz_stream struct. // level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. // level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. // (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) // Return values: // MZ_OK on success. // MZ_STREAM_ERROR if the stream is bogus. // MZ_PARAM_ERROR if the input parameters are bogus. // MZ_MEM_ERROR on out of memory. int mz_deflateInit(mz_streamp pStream, int level); // mz_deflateInit2() is like mz_deflate(), except with more control: // Additional parameters: // method must be MZ_DEFLATED // window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) // mem_level must be between [1, 9] (it's checked but ignored by miniz.c) int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); // Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). int mz_deflateReset(mz_streamp pStream); // mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. // Parameters: // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. // flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. // Return values: // MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). // MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. // MZ_STREAM_ERROR if the stream is bogus. // MZ_PARAM_ERROR if one of the parameters is invalid. // MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) int mz_deflate(mz_streamp pStream, int flush); // mz_deflateEnd() deinitializes a compressor: // Return values: // MZ_OK on success. // MZ_STREAM_ERROR if the stream is bogus. int mz_deflateEnd(mz_streamp pStream); // mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); // Single-call compression functions mz_compress() and mz_compress2(): // Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); // mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). mz_ulong mz_compressBound(mz_ulong source_len); // Initializes a decompressor. int mz_inflateInit(mz_streamp pStream); // mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: // window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). int mz_inflateInit2(mz_streamp pStream, int window_bits); // Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. // Parameters: // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. // flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. // On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). // MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. // Return values: // MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. // MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. // MZ_STREAM_ERROR if the stream is bogus. // MZ_DATA_ERROR if the deflate stream is invalid. // MZ_PARAM_ERROR if one of the parameters is invalid. // MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again // with more input data, or with more room in the output buffer (except when using single call decompression, described above). int mz_inflate(mz_streamp pStream, int flush); // Deinitializes a decompressor. int mz_inflateEnd(mz_streamp pStream); // Single-call decompression. // Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); // Returns a string description of the specified error code, or NULL if the error code is invalid. const char *mz_error(int err); // Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. // Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES #endif // MINIZ_NO_ZLIB_APIS // ------------------- Types and macros typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef long long mz_int64; typedef unsigned long long mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) // An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif // ------------------- ZIP archive reading/writing #ifndef MINIZ_NO_ARCHIVE_APIS enum { MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 }; typedef struct { mz_uint32 m_file_index; mz_uint32 m_central_dir_ofs; mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; #ifndef MINIZ_NO_TIME time_t m_time; #endif mz_uint32 m_crc32; mz_uint64 m_comp_size; mz_uint64 m_uncomp_size; mz_uint16 m_internal_attr; mz_uint32 m_external_attr; mz_uint64 m_local_header_ofs; mz_uint32 m_comment_size; char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef struct mz_zip_archive_tag { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; mz_uint m_total_files; mz_zip_mode m_zip_mode; mz_uint m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 } mz_zip_flags; // ZIP archive reading // Inits a ZIP archive reader. // These functions read and validate the archive's central directory. mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); #endif // Returns the total number of files in the archive. mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); // Returns detailed information about an archive file entry. mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); // Determines if an archive file entry is a directory entry. mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); // Retrieves the filename of an archive file entry. // Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); // Attempts to locates a file in the archive's central directory. // Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH // Returns -1 if the file cannot be found. int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); // Extracts a archive file to a memory buffer using no memory allocation. mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); // Extracts a archive file to a memory buffer. mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); // Extracts a archive file to a dynamically allocated heap buffer. void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); // Extracts a archive file using a callback function to output the file's data. mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); #ifndef MINIZ_NO_STDIO // Extracts a archive file to a disk file and sets its last accessed and modified times. // This function only extracts files, not archive directory records. mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); #endif // Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. mz_bool mz_zip_reader_end(mz_zip_archive *pZip); // ZIP archive writing #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS // Inits a ZIP archive writer. mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); #endif // Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. // For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. // For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). // Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. // Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before // the archive is finalized the file's central directory will be hosed. mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); // Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. // To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); #ifndef MINIZ_NO_STDIO // Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); #endif // Adds a file to an archive by fully cloning the data from another archive. // This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); // Finalizes the archive by writing the central directory records followed by the end of central directory record. // After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). // An archive must be manually finalized by calling this function for it to be valid. mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); // Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. // Note for the archive to be valid, it must have been finalized before ending. mz_bool mz_zip_writer_end(mz_zip_archive *pZip); // Misc. high-level helper functions: // mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); // Reads a single file from an archive into a heap block. // Returns NULL on failure. void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS #endif // #ifndef MINIZ_NO_ARCHIVE_APIS // ------------------- Low-level Decompression API Definitions // Decompression flags used by tinfl_decompress(). // TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. // TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. // TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). // TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; // High level decompression functions: // tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). // On entry: // pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. // On return: // Function returns a pointer to the decompressed data, or NULL on failure. // *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. // The caller must call mz_free() on the returned block when it's no longer needed. void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); // tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. // Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); // tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. // Returns 1 on success or 0 on failure. typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; // Max size of LZ dictionary. #define TINFL_LZ_DICT_SIZE 32768 // Return status. typedef enum { TINFL_STATUS_BAD_PARAM = -3, TINFL_STATUS_ADLER32_MISMATCH = -2, TINFL_STATUS_FAILED = -1, TINFL_STATUS_DONE = 0, TINFL_STATUS_NEEDS_MORE_INPUT = 1, TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; // Initializes the decompressor to its initial state. #define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 // Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. // This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); // Internal/private bits follow. enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; // ------------------- Low-level Compression API Definitions // Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). #define TDEFL_LESS_MEMORY 0 // tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): // TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; // TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. // TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). // TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. // TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). // TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) // TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. // TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. // TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. // The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; // High level compression functions: // tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). // On entry: // pSrc_buf, src_buf_len: Pointer and size of source block to compress. // flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. // On return: // Function returns a pointer to the compressed data, or NULL on failure. // *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. // The caller must free() the returned block when it's no longer needed. void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); // tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. // Returns 0 on failure. size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); // Compresses an image to a compressed PNG file in memory. // On entry: // pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. // The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. // level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL // If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). // On return: // Function returns a pointer to the compressed data, or NULL on failure. // *pLen_out will be set to the size of the PNG image file. // The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); // Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); // tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; // TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif // The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1, } tdefl_status; // Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; // tdefl's compression state structure. typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; // Initializes the compressor. // There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. // pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. // If pBut_buf_func is NULL the user should always call the tdefl_compress() API. // flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); // Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); // tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. // tdefl_compress_buffer() always consumes the entire input buffer. tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); mz_uint32 tdefl_get_adler32(tdefl_compressor *d); // Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. #ifndef MINIZ_NO_ZLIB_APIS // Create tdefl_compress() flags given zlib-style compression parameters. // level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) // window_bits may be -15 (raw deflate) or 15 (zlib) // strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #endif // #ifndef MINIZ_NO_ZLIB_APIS #ifdef __cplusplus } #endif #endif // MINIZ_HEADER_INCLUDED // ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) #ifndef MINIZ_HEADER_FILE_ONLY typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; #include <string.h> #include <assert.h> #define MZ_ASSERT(x) assert(x) #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a,b) (((a)>(b))?(a):(b)) #define MZ_MIN(a,b) (((a)<(b))?(a):(b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif // ------------------- zlib-style API's mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } // Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } void mz_free(void *p) { MZ_FREE(p); } #ifndef MINIZ_NO_ZLIB_APIS static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = def_alloc_func; if (!pStream->zfree) pStream->zfree = def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for ( ; ; ) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; // Can't make forward progress without some input. } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); // In case mz_ulong is 64-bits (argh I hate longs). if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = def_alloc_func; if (!pStream->zfree) pStream->zfree = def_free_func; pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflate(mz_streamp pStream, int flush) { inflate_state* pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state*)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } // flush != MZ_FINISH then we must assume there's more input. if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for ( ; ; ) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. else if (flush == MZ_FINISH) { // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); // In case mz_ulong is 64-bits (argh I hate longs). if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = { { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } }; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif //MINIZ_NO_ZLIB_APIS // ------------------- Low-level Decompression (completely independent from all compression API's) #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN switch(r->m_state) { case 0: #define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END #define TINFL_CR_FINISH } // TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never // reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. #define TINFL_GET_BYTE(state_index, c) do { \ if (pIn_buf_cur >= pIn_buf_end) { \ for ( ; ; ) { \ if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ if (pIn_buf_cur < pIn_buf_end) { \ c = *pIn_buf_cur++; \ break; \ } \ } else { \ c = 0; \ break; \ } \ } \ } else c = *pIn_buf_cur++; } MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END // TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. // It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a // Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the // bit buffer contains >=15 bits (deflate's max. Huffman code size). #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do { \ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ } while (num_bits < 15); // TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read // beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully // decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. // The slow path is only executed at the very end of the input buffer. #define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ int temp; mz_uint code_len, c; \ if (num_bits < 15) { \ if ((pIn_buf_end - pIn_buf_cur) < 2) { \ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } else { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ } \ } \ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else { \ code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); } else { TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); } } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for ( ; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) { mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for ( ; ; ) { mz_uint8 *pSrc; for ( ; ; ) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif do { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; } while ((int)(counter -= 3) > 2); if ((int)counter > 0) { pOut_buf_cur[0] = pSrc[0]; if ((int)counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } // Higher level helper functions. void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for ( ; ; ) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; tinfl_init(&decomp); for ( ; ; ) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } // ------------------- Low-level Compression (independent from all decompression API's) // Purposely making these tables static for faster init and thread safety. static const mz_uint16 s_tdefl_len_sym[256] = { 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; static const mz_uint8 s_tdefl_len_extra[256] = { 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7 }; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; // Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32* pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } // tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next=1; next < n-1; next++) { if (leaf>=n || A[root].m_key<A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key; if (leaf>=n || (root<next && A[root].m_key<A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); } A[n-2].m_key = 0; for (next=n-3; next>=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; avbl = 1; used = dpth = 0; root = n-2; next = n-1; while (avbl>0) { while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2*used; dpth++; used = 0; } } // Limits canonical Huffman code table's max code size. enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) do { \ mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ while (d->m_bits_in >= 8) { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ if (rle_repeat_count < 3) { \ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } else { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ } rle_repeat_count = 0; } } #define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ if (rle_z_count < 3) { \ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ } else if (rle_z_count <= 10) { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ } else { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ } rle_z_count = 0; } } static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); // This sequence coaxes MSVC into using cmov's vs. jmp's. s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; *(mz_uint64*)pOutput_buf = bit_buffer; pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static mz_bool tdefl_compress_fast(tdefl_compressor *d) { // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; // Simple lazy/greedy parsing state machine. len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } // Move the lookahead forward by len_to_move bytes. d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); // Check if it's time to flush the current LZ codes to the internal output buffer. if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } #ifndef MINIZ_NO_ZLIB_APIS static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; // level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #endif //MINIZ_NO_ZLIB_APIS #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) #endif // Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at // http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. // This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } // write dummy header for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); // compress image data tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } // write real header *pLen_out = out_buf.m_size-41; { static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); memcpy(out_buf.m_pBuf, pnghdr, 41); } // write footer (IDAT CRC-32, followed by IEND chunk) if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); // compute final size of file, grab compressed data buffer and return *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) { // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } #ifdef _MSC_VER #pragma warning (pop) #endif // ------------------- .ZIP archive reading #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include <stdio.h> #include <sys/stat.h> #if defined(_MSC_VER) || defined(__MINGW64__) static FILE *mz_fopen(const char *pFilename, const char *pMode) { FILE* pFile = NULL; fopen_s(&pFile, pFilename, pMode); return pFile; } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { FILE* pFile = NULL; if (freopen_s(&pFile, pPath, pMode, pStream)) return NULL; return pFile; } #ifndef MINIZ_NO_TIME #include <sys/utime.h> #endif #define MZ_FILE FILE #define MZ_FOPEN mz_fopen #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove #elif defined(__MINGW32__) #ifndef MINIZ_NO_TIME #include <sys/utime.h> #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include <sys/utime.h> #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__GNUC__) && _LARGEFILE64_SOURCE #ifndef MINIZ_NO_TIME #include <utime.h> #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen64(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT stat64 #define MZ_FILE_STAT stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove #else #ifndef MINIZ_NO_TIME #include <utime.h> #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #endif // #ifdef _MSC_VER #endif // #ifdef MINIZ_NO_STDIO #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) // Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. enum { // ZIP archive identifiers and record sizes MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, // Central directory header record offsets MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, // Local directory header offsets MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, // End of central directory offsets MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, }; typedef struct { void *m_p; size_t m_size, m_capacity; mz_uint m_element_size; } mz_zip_array; struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; MZ_FILE *m_pFile; void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; }; #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) { pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); memset(pArray, 0, sizeof(mz_zip_array)); } static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) { void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) { if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } pArray->m_size = new_size; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; } #ifndef MINIZ_NO_TIME static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; return mktime(&tm); } static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; errno_t err = localtime_s(tm, &time); if (err) { *pDOS_date = 0; *pDOS_time = 0; return; } #else struct tm *tm = localtime(&time); #endif *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } #endif #ifndef MINIZ_NO_STDIO static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef MINIZ_NO_TIME (void)pFilename; *pDOS_date = *pDOS_time = 0; #else struct MZ_FILE_STAT_STRUCT file_stat; // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); #endif // #ifdef MINIZ_NO_TIME return MZ_TRUE; } #ifndef MINIZ_NO_TIME static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) { struct utimbuf t; t.actime = access_time; t.modtime = modified_time; return !utime(pFilename, &t); } #endif // #ifndef MINIZ_NO_TIME #endif // #ifndef MINIZ_NO_STDIO static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) { (void)flags; if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return MZ_FALSE; if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; pZip->m_zip_mode = MZ_ZIP_MODE_READING; pZip->m_archive_size = 0; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return MZ_FALSE; memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (l_len < r_len) : (l < r); } #define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END // Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const int size = pZip->m_total_files; int start = (size - 2) >> 1, end; while (start >= 0) { int child, root = start; for ( ; ; ) { if ((child = (root << 1) + 1) >= size) break; child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } start--; } end = size - 1; while (end > 0) { int child, root = 0; MZ_SWAP_UINT32(pIndices[end], pIndices[0]); for ( ; ; ) { if ((child = (root << 1) + 1) >= end) break; child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } end--; } } static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) { mz_uint cdir_size, num_this_disk, cdir_disk_index; mz_uint64 cdir_ofs; mz_int64 cur_file_ofs; const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; // Find the end of central directory record by scanning the file from the end towards the beginning. cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for ( ; ; ) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; for (i = n - 4; i >= 0; --i) if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) break; if (i >= 0) { cur_file_ofs += i; break; } if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } // Read and verify the end of central directory record. if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) return MZ_FALSE; num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return MZ_FALSE; if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return MZ_FALSE; pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) return MZ_FALSE; if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) return MZ_FALSE; } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return MZ_FALSE; // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { mz_uint total_header_size, comp_size, decomp_size, disk_index; if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) return MZ_FALSE; MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) return MZ_FALSE; disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); if ((disk_index != num_this_disk) && (disk_index != 1)) return MZ_FALSE; if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) return MZ_FALSE; if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) return MZ_FALSE; n -= total_header_size; p += total_header_size; } } if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); return MZ_TRUE; } mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) { if ((!pZip) || (!pZip->m_pRead)) return MZ_FALSE; if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_archive_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); return s; } mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) { if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_archive_size = size; pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; #ifdef __cplusplus pZip->m_pState->m_pMem = const_cast<void *>(pMem); #else pZip->m_pState->m_pMem = (void *)pMem; #endif pZip->m_pState->m_mem_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) { mz_uint64 file_size; MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); if (!pFile) return MZ_FALSE; if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return MZ_FALSE; } file_size = MZ_FTELL64(pFile); if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { return pZip ? pZip->m_total_files : 0; } static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return NULL; return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { mz_uint m_bit_flag; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) return MZ_FALSE; m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); return (m_bit_flag & 1); } mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) { mz_uint filename_len, external_attr; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) return MZ_FALSE; // First see if the filename ends with a '/' character. filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_len) { if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; } // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. // FIXME: Remove this check? Is it necessary - we already check the filename. external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); if ((external_attr & 0x10) != 0) return MZ_TRUE; return MZ_FALSE; } mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) { mz_uint n; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if ((!p) || (!pStat)) return MZ_FALSE; // Unpack the central directory record. pStat->m_file_index = file_index; pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); #ifndef MINIZ_NO_TIME pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); #endif pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); // Copy as much of the filename and comment as possible. n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); pStat->m_comment_size = n; memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; return MZ_TRUE; } mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) { mz_uint n; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_buf_size) { n = MZ_MIN(n, filename_buf_size - 1); memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pFilename[n] = '\0'; } return n + 1; } static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { mz_uint i; if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); for (i = 0; i < len; ++i) if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; return MZ_TRUE; } static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (int)(l_len - r_len) : (l - r); } static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const int size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); int l = 0, h = size - 1; while (l <= h) { int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) return file_index; else if (comp < 0) l = m + 1; else h = m - 1; } return -1; } int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { mz_uint file_index; size_t name_len, comment_len; if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return -1; if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) return mz_zip_reader_locate_file_binary_search(pZip, pName); name_len = strlen(pName); if (name_len > 0xFFFF) return -1; comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; for (file_index = 0; file_index < pZip->m_total_files; file_index++) { const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (filename_len < name_len) continue; if (comment_len) { mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); const char *pFile_comment = pFilename + filename_len + file_extra_len; if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) continue; } if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { int ofs = filename_len - 1; do { if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; } while (--ofs >= 0); ofs++; pFilename += ofs; filename_len -= ofs; } if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) return file_index; } return -1; } mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; mz_zip_archive_file_stat file_stat; void *pRead_buf; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; tinfl_decompressor inflator; if ((buf_size) && (!pBuf)) return MZ_FALSE; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) if (!file_stat.m_comp_size) return MZ_TRUE; // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). // I'm torn how to handle this case - should it fail instead? if (mz_zip_reader_is_file_a_directory(pZip, file_index)) return MZ_TRUE; // Encryption and patch files are not supported. if (file_stat.m_bit_flag & (1 | 32)) return MZ_FALSE; // This function only supports stored and deflate. if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return MZ_FALSE; // Ensure supplied output buffer is large enough. needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (buf_size < needed_size) return MZ_FALSE; // Read and parse the local directory entry. cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return MZ_FALSE; if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { // The file is stored or the caller has requested the compressed data. if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) return MZ_FALSE; return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); } // Decompress the file either directly from memory or from a file input buffer. tinfl_init(&inflator); if (pZip->m_pState->m_pMem) { // Read directly from the archive in memory. pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else if (pUser_read_buf) { // Use a user provided read buffer. if (!user_read_buf_size) return MZ_FALSE; pRead_buf = (mz_uint8 *)pUser_read_buf; read_buf_size = user_read_buf_size; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } else { // Temporarily allocate a read buffer. read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) #endif return MZ_FALSE; if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } do { size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; out_buf_ofs += out_buf_size; } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); if (status == TINFL_STATUS_DONE) { // Make sure the entire file was decompressed, and check its CRC. if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) status = TINFL_STATUS_FAILED; } if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); } void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { mz_uint64 comp_size, uncomp_size, alloc_size; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); void *pBuf; if (pSize) *pSize = 0; if (!p) return NULL; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) #endif return NULL; if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) return NULL; if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; } if (pSize) *pSize = (size_t)alloc_size; return pBuf; } void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) { if (pSize) *pSize = 0; return MZ_FALSE; } return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); } mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf = NULL; void *pWrite_buf = NULL; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) if (!file_stat.m_comp_size) return MZ_TRUE; // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). // I'm torn how to handle this case - should it fail instead? if (mz_zip_reader_is_file_a_directory(pZip, file_index)) return MZ_TRUE; // Encryption and patch files are not supported. if (file_stat.m_bit_flag & (1 | 32)) return MZ_FALSE; // This function only supports stored and deflate. if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return MZ_FALSE; // Read and parse the local directory entry. cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return MZ_FALSE; // Decompress the file either directly from memory or from a file input buffer. if (pZip->m_pState->m_pMem) { pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else { read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { // The file is stored or the caller has requested the compressed data. if (pZip->m_pState->m_pMem) { #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) #endif return MZ_FALSE; if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) status = TINFL_STATUS_FAILED; else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); cur_file_ofs += file_stat.m_comp_size; out_buf_ofs += file_stat.m_comp_size; comp_remaining = 0; } else { while (comp_remaining) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; out_buf_ofs += read_buf_avail; comp_remaining -= read_buf_avail; } } } else { tinfl_decompressor inflator; tinfl_init(&inflator); if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) status = TINFL_STATUS_FAILED; else { do { mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; if (out_buf_size) { if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { status = TINFL_STATUS_FAILED; break; } file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { status = TINFL_STATUS_FAILED; break; } } } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); } } if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { // Make sure the entire file was decompressed, and check its CRC. if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) status = TINFL_STATUS_FAILED; } if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); } mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) { mz_bool status; mz_zip_archive_file_stat file_stat; MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return MZ_FALSE; status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); if (MZ_FCLOSE(pFile) == EOF) return MZ_FALSE; #ifndef MINIZ_NO_TIME if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif return status; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return MZ_FALSE; if (pZip->m_pState) { mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { MZ_FCLOSE(pState->m_pFile); pState->m_pFile = NULL; } #endif // #ifndef MINIZ_NO_STDIO pZip->m_pFree(pZip->m_pAlloc_opaque, pState); } pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } #endif // ------------------- .ZIP archive writing #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return MZ_FALSE; if (pZip->m_file_offset_alignment) { // Ensure user specified file offset alignment is a power of 2. if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) return MZ_FALSE; } if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; pZip->m_archive_size = existing_size; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return MZ_FALSE; memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); return MZ_TRUE; } static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); #ifdef _MSC_VER if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) #else if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) #endif return 0; if (new_size > pState->m_mem_capacity) { void *pNew_block; size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) return 0; pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; } memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); pState->m_mem_size = (size_t)new_size; return n; } mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) return MZ_FALSE; if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { mz_zip_writer_end(pZip); return MZ_FALSE; } pZip->m_pState->m_mem_capacity = initial_allocation_size; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) { MZ_FILE *pFile; pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) return MZ_FALSE; if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) { mz_zip_writer_end(pZip); return MZ_FALSE; } pZip->m_pState->m_pFile = pFile; if (size_to_reserve_at_beginning) { mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); do { size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { mz_zip_writer_end(pZip); return MZ_FALSE; } cur_ofs += n; size_to_reserve_at_beginning -= n; } while (size_to_reserve_at_beginning); } return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return MZ_FALSE; // No sense in trying to write to an archive that's already at the support max size if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO pFilename; return MZ_FALSE; #else // Archive is being read from stdio - try to reopen as writable. if (pZip->m_pIO_opaque != pZip) return MZ_FALSE; if (!pFilename) return MZ_FALSE; pZip->m_pWrite = mz_zip_file_write_func; if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. mz_zip_reader_end(pZip); return MZ_FALSE; } #endif // #ifdef MINIZ_NO_STDIO } else if (pState->m_pMem) { // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. if (pZip->m_pIO_opaque != pZip) return MZ_FALSE; pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; } // Archive is being read via a user provided read function - make sure the user has specified a write function too. else if (!pZip->m_pWrite) return MZ_FALSE; // Start writing new files at the archive's current central directory location. pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; pZip->m_central_directory_file_ofs = 0; return MZ_TRUE; } mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) { return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); } typedef struct { mz_zip_archive *m_pZip; mz_uint64 m_cur_archive_file_ofs; mz_uint64 m_comp_size; } mz_zip_writer_add_state; static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) { mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) return MZ_FALSE; pState->m_cur_archive_file_ofs += len; pState->m_comp_size += len; return MZ_TRUE; } static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; } static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); return MZ_TRUE; } static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; size_t orig_central_dir_size = pState->m_central_dir.m_size; mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; // No zip64 support yet if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) return MZ_FALSE; if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { // Try to push the central directory array back into its original state. mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. if (*pArchive_name == '/') return MZ_FALSE; while (*pArchive_name) { if ((*pArchive_name == '\\') || (*pArchive_name == ':')) return MZ_FALSE; pArchive_name++; } return MZ_TRUE; } static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { mz_uint32 n; if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); } static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { char buf[4096]; memset(buf, 0, MZ_MIN(sizeof(buf), n)); while (n) { mz_uint32 s = MZ_MIN(sizeof(buf), n); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) return MZ_FALSE; cur_file_ofs += s; n -= s; } return MZ_TRUE; } mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; tdefl_compressor *pComp = NULL; mz_bool store_data_uncompressed; mz_zip_internal_state *pState; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) return MZ_FALSE; pState = pZip->m_pState; if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) return MZ_FALSE; // No zip64 support yet if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; #ifndef MINIZ_NO_TIME { time_t cur_time; time(&cur_time); mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); } #endif // #ifndef MINIZ_NO_TIME archive_name_size = strlen(pArchive_name); if (archive_name_size > 0xFFFF) return MZ_FALSE; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { // Set DOS Subdirectory attribute bit. ext_attributes |= 0x10; // Subdirectories cannot contain data. if ((buf_size) || (uncomp_size)) return MZ_FALSE; } // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) return MZ_FALSE; if ((!store_data_uncompressed) && (buf_size)) { if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) return MZ_FALSE; } if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); MZ_CLEAR_OBJ(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } cur_archive_file_ofs += archive_name_size; if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); uncomp_size = buf_size; if (uncomp_size <= 3) { level = 0; store_data_uncompressed = MZ_TRUE; } } if (store_data_uncompressed) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } cur_archive_file_ofs += buf_size; comp_size = buf_size; if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) method = MZ_DEFLATED; } else if (buf_size) { mz_zip_writer_add_state state; state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; method = MZ_DEFLATED; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pComp = NULL; // no zip64 support yet if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) return MZ_FALSE; if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return MZ_FALSE; if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; MZ_FILE *pSrc_file = NULL; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return MZ_FALSE; if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; archive_name_size = strlen(pArchive_name); if (archive_name_size > 0xFFFF) return MZ_FALSE; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) return MZ_FALSE; pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) return MZ_FALSE; MZ_FSEEK64(pSrc_file, 0, SEEK_END); uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); if (uncomp_size > 0xFFFFFFFF) { // No zip64 support yet MZ_FCLOSE(pSrc_file); return MZ_FALSE; } if (uncomp_size <= 3) level = 0; if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); MZ_CLEAR_OBJ(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } cur_archive_file_ofs += archive_name_size; if (uncomp_size) { mz_uint64 uncomp_remaining = uncomp_size; void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); if (!pRead_buf) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } if (!level) { while (uncomp_remaining) { mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); uncomp_remaining -= n; cur_archive_file_ofs += n; } comp_size = uncomp_size; } else { mz_bool result = MZ_FALSE; mz_zip_writer_add_state state; tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); if (!pComp) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } for ( ; ; ) { size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); tdefl_status status; if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) break; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); uncomp_remaining -= in_buf_size; status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); if (status == TDEFL_STATUS_DONE) { result = MZ_TRUE; break; } else if (status != TDEFL_STATUS_OKAY) break; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); if (!result) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; method = MZ_DEFLATED; } pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } MZ_FCLOSE(pSrc_file); pSrc_file = NULL; // no zip64 support yet if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) return MZ_FALSE; if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return MZ_FALSE; if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) { mz_uint n, bit_flags, num_alignment_padding_bytes; mz_uint64 comp_bytes_remaining, local_dir_header_ofs; mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; size_t orig_central_dir_size; mz_zip_internal_state *pState; void *pBuf; const mz_uint8 *pSrc_central_header; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return MZ_FALSE; if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) return MZ_FALSE; pState = pZip->m_pState; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); cur_dst_file_ofs = pZip->m_archive_size; if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) return MZ_FALSE; cur_dst_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_dst_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) return MZ_FALSE; while (comp_bytes_remaining) { n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_src_file_ofs += n; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_dst_file_ofs += n; comp_bytes_remaining -= n; } bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); if (bit_flags & 8) { // Copy data descriptor if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_src_file_ofs += n; cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); // no zip64 support yet if (cur_dst_file_ofs > 0xFFFFFFFF) return MZ_FALSE; orig_central_dir_size = pState->m_central_dir.m_size; memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) return MZ_FALSE; n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } if (pState->m_central_dir.m_size > 0xFFFFFFFF) return MZ_FALSE; n = (mz_uint32)orig_central_dir_size; if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } pZip->m_total_files++; pZip->m_archive_size = cur_dst_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_uint64 central_dir_ofs, central_dir_size; mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return MZ_FALSE; pState = pZip->m_pState; // no zip64 support yet if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; central_dir_ofs = 0; central_dir_size = 0; if (pZip->m_total_files) { // Write central directory central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return MZ_FALSE; pZip->m_archive_size += central_dir_size; } // Write end of central directory record MZ_CLEAR_OBJ(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) return MZ_FALSE; #ifndef MINIZ_NO_STDIO if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) return MZ_FALSE; #endif // #ifndef MINIZ_NO_STDIO pZip->m_archive_size += sizeof(hdr); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) { if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) return MZ_FALSE; if (pZip->m_pWrite != mz_zip_heap_write_func) return MZ_FALSE; if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; *pBuf = pZip->m_pState->m_pMem; *pSize = pZip->m_pState->m_mem_size; pZip->m_pState->m_pMem = NULL; pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; return MZ_TRUE; } mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_bool status = MZ_TRUE; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) return MZ_FALSE; pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { MZ_FCLOSE(pState->m_pFile); pState->m_pFile = NULL; } #endif // #ifndef MINIZ_NO_STDIO if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); pState->m_pMem = NULL; } pZip->m_pFree(pZip->m_pAlloc_opaque, pState); pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { mz_bool status, created_new_archive = MZ_FALSE; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; MZ_CLEAR_OBJ(zip_archive); if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { // Create a new archive. if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) return MZ_FALSE; created_new_archive = MZ_TRUE; } else { // Append to an existing archive. if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) return MZ_FALSE; if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) { mz_zip_reader_end(&zip_archive); return MZ_FALSE; } } status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) if (!mz_zip_writer_finalize_archive(&zip_archive)) status = MZ_FALSE; if (!mz_zip_writer_end(&zip_archive)) status = MZ_FALSE; if ((!status) && (created_new_archive)) { // It's a new archive and something went wrong, so just delete it. int ignoredStatus = MZ_DELETE_FILE(pZip_filename); (void)ignoredStatus; } return status; } void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) { int file_index; mz_zip_archive zip_archive; void *p = NULL; if (pSize) *pSize = 0; if ((!pZip_filename) || (!pArchive_name)) return NULL; MZ_CLEAR_OBJ(zip_archive); if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) return NULL; if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); mz_zip_reader_end(&zip_archive); return p; } #endif // #ifndef MINIZ_NO_STDIO #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS #endif // #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus } #endif #endif // MINIZ_HEADER_FILE_ONLY /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to <http://unlicense.org/> */ |
Added src/mkbuiltin.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This is a stand-alone utility program that is part of the Fossil build ** process. This program reads files named on the command line and converts ** them into ANSI-C static char array variables. Output is written onto ** standard output. ** ** The makefiles use this utility package various resources (large scripts, ** GIF images, etc) that are separate files in the source code as byte ** arrays in the resulting executable. */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* ** Read the entire content of the file named zFilename into memory obtained ** from malloc() and return a pointer to that memory. Write the size of the ** file into *pnByte. */ static unsigned char *read_file(const char *zFilename, int *pnByte){ FILE *in; unsigned char *z; int nByte; int got; in = fopen(zFilename, "rb"); if( in==0 ){ return 0; } fseek(in, 0, SEEK_END); *pnByte = nByte = ftell(in); fseek(in, 0, SEEK_SET); z = malloc( nByte+1 ); if( z==0 ){ fprintf(stderr, "failed to allocate %d bytes\n", nByte+1); exit(1); } got = fread(z, 1, nByte, in); fclose(in); z[got] = 0; return z; } /* ** There is an instance of the following for each file translated. */ typedef struct Resource Resource; struct Resource { const char *zName; int nByte; int idx; }; /* ** Compare two Resource objects for sorting purposes. They sort ** in zName order so that Fossil can search for resources using ** a binary search. */ static int compareResource(const void *a, const void *b){ Resource *pA = (Resource*)a; Resource *pB = (Resource*)b; return strcmp(pA->zName, pB->zName); } int main(int argc, char **argv){ int i, sz; int j, n; Resource *aRes; int nRes; unsigned char *pData; int nErr = 0; int nSkip; int nPrefix = 0; if( argc>3 && strcmp(argv[1],"--prefix")==0 ){ nPrefix = (int)strlen(argv[2]); argc -= 2; argv += 2; } nRes = argc - 1; aRes = malloc( nRes*sizeof(aRes[0]) ); if( aRes==0 ){ fprintf(stderr, "malloc failed\n"); return 1; } for(i=0; i<argc-1; i++){ aRes[i].zName = argv[i+1]; } qsort(aRes, nRes, sizeof(aRes[0]), compareResource); printf("/* Automatically generated code: Do not edit.\n**\n" "** Rerun the \"mkbuiltin.c\" program or rerun the Fossil\n" "** makefile to update this source file.\n" "*/\n"); for(i=0; i<nRes; i++){ pData = read_file(aRes[i].zName, &sz); if( pData==0 ){ fprintf(stderr, "Cannot open file [%s]\n", aRes[i].zName); nErr++; continue; } /* Skip initial lines beginning with # */ nSkip = 0; while( pData[nSkip]=='#' ){ while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; } if( pData[nSkip]=='\n' ) nSkip++; } aRes[i].nByte = sz - nSkip; aRes[i].idx = i; printf("/* Content of file %s */\n", aRes[i].zName); printf("static const unsigned char bidata%d[%d] = {\n ", i, sz+1-nSkip); for(j=nSkip, n=0; j<=sz; j++){ printf("%3d", pData[j]); if( j==sz ){ printf(" };\n"); }else if( n==14 ){ printf(",\n "); n = 0; }else{ printf(", "); n++; } } free(pData); } printf("typedef struct BuiltinFileTable BuiltinFileTable;\n"); printf("struct BuiltinFileTable {\n"); printf(" const char *zName;\n"); printf(" const unsigned char *pData;\n"); printf(" int nByte;\n"); printf("};\n"); printf("static const BuiltinFileTable aBuiltinFiles[] = {\n"); for(i=0; i<nRes; i++){ const char *z = aRes[i].zName; if( strlen(z)>=nPrefix ) z += nPrefix; while( z[0]=='.' || z[0]=='/' ){ z++; } aRes[i].zName = z; } qsort(aRes, nRes, sizeof(aRes[0]), compareResource); for(i=0; i<nRes; i++){ printf(" { \"%s\", bidata%d, %d },\n", aRes[i].zName, aRes[i].idx, aRes[i].nByte); } printf("};\n"); return nErr; } |
Changes to src/mkindex.c.
︙ | ︙ | |||
215 216 217 218 219 220 221 | i+=j; while( isspace(zLine[i]) ){ i++; } if( zLine[i]!='(' ) goto page_skip; nFixed = nUsed; nHelp = 0; return; | | | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | i+=j; while( isspace(zLine[i]) ){ i++; } if( zLine[i]!='(' ) goto page_skip; nFixed = nUsed; nHelp = 0; return; page_skip: for(i=nFixed; i<nUsed; i++){ fprintf(stderr,"%s:%d: skipping page \"%s\"\n", zFile, nLine, aEntry[i].zPath); } nUsed = nFixed; } |
︙ | ︙ | |||
281 282 283 284 285 286 287 | printf( "static const NameMap aCommand[] = {\n" ); for(i=0; i<nFixed /*&& aEntry[i].eType==1*/; i++){ const char *z = aEntry[i].zPath; int n = strlen(z); int cmdFlags = (1==aEntry[i].eType) ? 0x01 : 0x08; | | > | 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | printf( "static const NameMap aCommand[] = {\n" ); for(i=0; i<nFixed /*&& aEntry[i].eType==1*/; i++){ const char *z = aEntry[i].zPath; int n = strlen(z); int cmdFlags = (1==aEntry[i].eType) ? 0x01 : 0x08; if( 0x01==cmdFlags ){ if( z[n-1]=='*' ){ n--; cmdFlags = 0x02; }else if( memcmp(z, "test-", 5)==0 ){ cmdFlags = 0x04; } } if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); printf(" { \"%s%.*s\",%*s %s,%*s %d },\n", (0x08 & cmdFlags) ? "/" : "", n, z, 25-n, "", aEntry[i].zFunc, (int)(35-strlen(aEntry[i].zFunc)), "", cmdFlags ); if( aEntry[i].zIf ) printf("#endif\n"); } printf("};\n"); printf("#define FOSSIL_FIRST_CMD (sizeof(aWebpage)/sizeof(aWebpage[0]))\n"); for(i=0; i<nFixed; i++){ char *z = aEntry[i].zHelp; if( z && z[0] ){ if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); printf("static const char zHelp_%s[] = \n", aEntry[i].zFunc); printf(" \""); while( *z ){ |
︙ | ︙ | |||
324 325 326 327 328 329 330 | printf("\";\n"); if( aEntry[i].zIf ) printf("#endif\n"); aEntry[i].zHelp[0] = 0; } } puts("struct CmdHelp {" "int eType; " | | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | printf("\";\n"); if( aEntry[i].zIf ) printf("#endif\n"); aEntry[i].zHelp[0] = 0; } } puts("struct CmdHelp {" "int eType; " "const char *zText;" "};"); puts("static struct CmdHelp aCmdHelp[] = {"); for(i=0; i<nFixed; i++){ if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); if( aEntry[i].zHelp==0 ){ printf("{%d, 0},\n", aEntry[i].eType); }else{ |
︙ | ︙ | |||
358 359 360 361 362 363 364 | nLine++; scan_for_if(zLine); scan_for_label("WEBPAGE:",zLine,0); scan_for_label("COMMAND:",zLine,1); scan_for_func(zLine); } fclose(in); | | | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | nLine++; scan_for_if(zLine); scan_for_label("WEBPAGE:",zLine,0); scan_for_label("COMMAND:",zLine,1); scan_for_func(zLine); } fclose(in); nUsed = nFixed; } int main(int argc, char **argv){ int i; for(i=1; i<argc; i++){ zFile = argv[i]; process_file(); } build_table(); return 0; } |
Changes to src/mkversion.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** This C program generates the "VERSION.h" header file from information ** extracted out of the "manifest", "manifest.uuid", and "VERSION" files. ** Call this program with three arguments: ** ** ./a.out manifest.uuid manifest VERSION ** ** Note that the manifest.uuid and manifest files are generated by Fossil. */ #include <stdio.h> #include <string.h> int main(int argc, char *argv[]){ FILE *m,*u,*v; char *z; int i, x, d; char b[1000]; char vx[1000]; memset(b,0,sizeof(b)); memset(vx,0,sizeof(vx)); u = fopen(argv[1],"r"); | > | > > > | > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | /* ** This C program generates the "VERSION.h" header file from information ** extracted out of the "manifest", "manifest.uuid", and "VERSION" files. ** Call this program with three arguments: ** ** ./a.out manifest.uuid manifest VERSION ** ** Note that the manifest.uuid and manifest files are generated by Fossil. */ #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, char *argv[]){ FILE *m,*u,*v; char *z; int i, x, d; char b[1000]; char vx[1000]; memset(b,0,sizeof(b)); memset(vx,0,sizeof(vx)); u = fopen(argv[1],"r"); if( fgets(b, sizeof(b)-1,u)==0 ){ fprintf(stderr, "malformed manifest.uuid file: %s\n", argv[1]); exit(1); } fclose(u); for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} *z = 0; printf("#define MANIFEST_UUID \"%s\"\n",b); printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b); m = fopen(argv[2],"r"); while(b == fgets(b, sizeof(b)-1,m)){ if(0 == strncmp("D ",b,2)){ printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13); printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2); } } fclose(m); v = fopen(argv[3],"r"); if( fgets(b, sizeof(b)-1,v)==0 ){ fprintf(stderr, "malformed VERSION file: %s\n", argv[3]); exit(1); } fclose(v); for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} *z = 0; printf("#define RELEASE_VERSION \"%s\"\n", b); x=0; i=0; z=b; while(1){ if( z[0]>='0' && z[0]<='9' ){ x = x*10 + z[0] - '0'; }else{ |
︙ | ︙ | |||
67 68 69 70 71 72 73 74 75 | z[0] = '\0'; break; } } printf("#define RELEASE_RESOURCE_VERSION %s", vx); while( d<3 ){ printf(",0"); d++; } printf("\n"); return 0; } | > > > > > > > > > > > > > > | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | z[0] = '\0'; break; } } printf("#define RELEASE_RESOURCE_VERSION %s", vx); while( d<3 ){ printf(",0"); d++; } printf("\n"); #if defined(__DMC__) /* e.g. 0x857 */ d = (__DMC__ & 0xF00) >> 8; /* major */ x = (__DMC__ & 0x0F0) >> 4; /* minor */ i = (__DMC__ & 0x00F); /* revision */ printf("#define COMPILER_VERSION \"%d.%d.%d\"\n", d, x, i); #elif defined(__POCC__) /* e.g. 700 */ d = (__POCC__ / 100); /* major */ x = (__POCC__ % 100); /* minor */ printf("#define COMPILER_VERSION \"%d.%02d\"\n", d, x); #elif defined(_MSC_VER) /* e.g. 1800 */ d = (_MSC_VER / 100); /* major */ x = (_MSC_VER % 100); /* minor */ printf("#define COMPILER_VERSION \"%d.%02d\"\n", d, x); #endif return 0; } |
Changes to src/moderate.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 | /* ** Create a table to represent pending moderation requests, if the ** table does not already exist. */ void moderation_table_create(void){ db_multi_exec( | | | < < < | < < | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | /* ** Create a table to represent pending moderation requests, if the ** table does not already exist. */ void moderation_table_create(void){ db_multi_exec( "CREATE TABLE IF NOT EXISTS %s.modreq(\n" " objid INTEGER PRIMARY KEY,\n" /* Record pending approval */ " attachRid INT,\n" /* Object attached */ " tktid TEXT\n" /* Associated ticket id */ ");\n", db_name("repository") ); } /* ** Return TRUE if the modreq table exists */ int moderation_table_exists(void){ return db_table_exists("repository", "modreq"); } /* ** Return TRUE if the object specified is being held for moderation. */ int moderation_pending(int rid){ static Stmt q; int rc; if( rid==0 || !moderation_table_exists() ) return 0; db_static_prepare(&q, "SELECT 1 FROM modreq WHERE objid=:objid"); db_bind_int(&q, ":objid", rid); rc = db_step(&q)==SQLITE_ROW; db_reset(&q); return rc; } /* ** Check to see if the object identified by RID is used for anything. */ static int object_used(int rid){ static const char *const aTabField[] = { "modreq", "attachRid", "mlink", "mid", "mlink", "fid", "tagxref", "srcid", "tagxref", "rid", }; int i; for(i=0; i<sizeof(aTabField)/sizeof(aTabField[0]); i+=2){ if( db_exists("SELECT 1 FROM \"%w\" WHERE \"%w\"=%d", aTabField[i], aTabField[i+1], rid) ) return 1; } return 0; } /* ** Delete a moderation item given by objid |
︙ | ︙ | |||
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | fossil_free(zTktid); } attachRid = db_int(0, "SELECT attachRid FROM modreq WHERE objid=%d", rid); if( rid==objid ){ db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid); } if( attachRid && object_used(attachRid) ) attachRid = 0; rid = attachRid; } db_end_transaction(0); } /* ** Approve an object held for moderation. */ void moderation_approve(int rid){ if( !moderation_pending(rid) ) return; db_begin_transaction(); db_multi_exec( "DELETE FROM private WHERE rid=%d;" "INSERT OR IGNORE INTO unclustered VALUES(%d);" "INSERT OR IGNORE INTO unsent VALUES(%d);", rid, rid, rid ); db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid); db_end_transaction(0); } /* ** WEBPAGE: modreq ** ** Show all pending moderation request */ void modreq_page(void){ Blob sql; Stmt q; login_check_credentials(); | > > | > > > | | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | fossil_free(zTktid); } attachRid = db_int(0, "SELECT attachRid FROM modreq WHERE objid=%d", rid); if( rid==objid ){ db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid); } if( attachRid && object_used(attachRid) ) attachRid = 0; admin_log("Disapproved moderation of rid %d.", rid); rid = attachRid; } db_end_transaction(0); } /* ** Approve an object held for moderation. */ void moderation_approve(int rid){ if( !moderation_pending(rid) ) return; db_begin_transaction(); db_multi_exec( "DELETE FROM private WHERE rid=%d;" "INSERT OR IGNORE INTO unclustered VALUES(%d);" "INSERT OR IGNORE INTO unsent VALUES(%d);", rid, rid, rid ); db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid); admin_log("Approved moderation of rid %d.", rid); db_end_transaction(0); } /* ** WEBPAGE: modreq ** ** Show all pending moderation request */ void modreq_page(void){ Blob sql; Stmt q; login_check_credentials(); if( !g.perm.RdWiki && !g.perm.RdTkt ){ login_needed(g.anon.RdWiki && g.anon.RdTkt); return; } style_header("Pending Moderation Requests"); @ <h2>All Pending Moderation Requests</h2> if( moderation_table_exists() ){ blob_init(&sql, timeline_query_for_www(), -1); blob_append_sql(&sql, " AND event.objid IN (SELECT objid FROM modreq)" " ORDER BY event.mtime DESC" ); db_prepare(&q, "%s", blob_sql_text(&sql)); www_print_timeline(&q, 0, 0, 0, 0, 0); db_finalize(&q); } style_footer(); } |
Changes to src/name.c.
︙ | ︙ | |||
15 16 17 18 19 20 21 | ** ******************************************************************************* ** ** This file contains code used to convert user-supplied object names into ** canonical UUIDs. ** ** A user-supplied object name is any unique prefix of a valid UUID but | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | ** ******************************************************************************* ** ** This file contains code used to convert user-supplied object names into ** canonical UUIDs. ** ** A user-supplied object name is any unique prefix of a valid UUID but ** not necessarily in canonical form. */ #include "config.h" #include "name.h" #include <assert.h> /* ** Return TRUE if the string begins with something that looks roughly |
︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | if( !fossil_isdigit(z[5]) ) return 0; if( !fossil_isdigit(z[6]) ) return 0; if( z[7]!='-') return 0; if( !fossil_isdigit(z[8]) ) return 0; if( !fossil_isdigit(z[9]) ) return 0; return 1; } /* ** Convert a symbolic name into a RID. Acceptable forms: ** ** * SHA1 hash ** * SHA1 hash prefix of at least 4 characters ** * Symbolic Name ** * "tag:" + symbolic name | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > | > > > > > | | | | | > | < < < < < < < < < < < < < < < < < < < < < < < < | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | if( !fossil_isdigit(z[5]) ) return 0; if( !fossil_isdigit(z[6]) ) return 0; if( z[7]!='-') return 0; if( !fossil_isdigit(z[8]) ) return 0; if( !fossil_isdigit(z[9]) ) return 0; return 1; } /* ** Return the RID that is the "root" of the branch that contains ** check-in "rid" if inBranch==0 or the first check-in in the branch ** if inBranch==1. */ int start_of_branch(int rid, int inBranch){ Stmt q; int rc; char *zBr; zBr = db_text("trunk","SELECT value FROM tagxref" " WHERE rid=%d AND tagid=%d" " AND tagtype>0", rid, TAG_BRANCH); db_prepare(&q, "SELECT pid, EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0" " AND value=%Q AND rid=plink.pid)" " FROM plink" " WHERE cid=:cid AND isprim", TAG_BRANCH, zBr ); fossil_free(zBr); do{ db_reset(&q); db_bind_int(&q, ":cid", rid); rc = db_step(&q); if( rc!=SQLITE_ROW ) break; if( inBranch && db_column_int(&q,1)==0 ) break; rid = db_column_int(&q, 0); }while( db_column_int(&q, 1)==1 && rid>0 ); db_finalize(&q); return rid; } /* ** Convert a symbolic name into a RID. Acceptable forms: ** ** * SHA1 hash ** * SHA1 hash prefix of at least 4 characters ** * Symbolic Name ** * "tag:" + symbolic name ** * Date or date-time ** * "date:" + Date or date-time ** * symbolic-name ":" date-time ** * "tip" ** ** The following additional forms are available in local checkouts: ** ** * "current" ** * "prev" or "previous" ** * "next" ** ** Return the RID of the matching artifact. Or return 0 if the name does not ** match any known object. Or return -1 if the name is ambiguous. ** ** The zType parameter specifies the type of artifact: ci, t, w, e, g. ** If zType is NULL or "" or "*" then any type of artifact will serve. ** If zType is "br" then find the first check-in of the named branch ** rather than the last. ** zType is "ci" in most use cases since we are usually searching for ** a check-in. ** ** Note that the input zTag for types "t" and "e" is the SHA1 hash of ** the ticket-change or event-change artifact, not the randomly generated ** hexadecimal identifier assigned to tickets and events. Those identifiers ** live in a separate namespace. */ int symbolic_name_to_rid(const char *zTag, const char *zType){ int vid; int rid = 0; int nTag; int i; int startOfBranch = 0; if( zType==0 || zType[0]==0 ){ zType = "*"; }else if( zType[0]=='b' ){ zType = "ci"; startOfBranch = 1; } if( zTag==0 || zTag[0]==0 ) return 0; /* special keyword: "tip" */ if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){ rid = db_int(0, "SELECT objid" " FROM event" " WHERE type='ci'" " ORDER BY event.mtime DESC" ); if( rid ) return rid; } /* special keywords: "prev", "previous", "current", and "next" */ if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ if( fossil_strcmp(zTag, "current")==0 ){ rid = vid; }else if( fossil_strcmp(zTag, "prev")==0 || fossil_strcmp(zTag, "previous")==0 ){ rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid); }else if( fossil_strcmp(zTag, "next")==0 ){ rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d" " ORDER BY isprim DESC, mtime DESC", vid); } if( rid ) return rid; } /* Date and times */ if( memcmp(zTag, "date:", 5)==0 ){ rid = db_int(0, "SELECT objid FROM event" " WHERE mtime<=julianday(%Q,'utc') AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", &zTag[5], zType); return rid; } if( fossil_isdate(zTag) ){ rid = db_int(0, "SELECT objid FROM event" " WHERE mtime<=julianday(%Q,'utc') AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", zTag, zType); if( rid) return rid; } /* Deprecated date & time formats: "local:" + date-time and ** "utc:" + date-time */ if( memcmp(zTag, "local:", 6)==0 ){ rid = db_int(0, "SELECT objid FROM event" " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", &zTag[6], zType); return rid; } if( memcmp(zTag, "utc:", 4)==0 ){ rid = db_int(0, "SELECT objid FROM event" " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" " ORDER BY mtime DESC LIMIT 1", &zTag[4], zType); return rid; } /* "tag:" + symbolic-name */ if( memcmp(zTag, "tag:", 4)==0 ){ rid = db_int(0, "SELECT event.objid, max(event.mtime)" " FROM tag, tagxref, event" " WHERE tag.tagname='sym-%q' " " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " " AND event.objid=tagxref.rid " " AND event.type GLOB '%q'", &zTag[4], zType ); if( startOfBranch ) rid = start_of_branch(rid,1); return rid; } /* root:TAG -> The origin of the branch */ if( memcmp(zTag, "root:", 5)==0 ){ rid = symbolic_name_to_rid(zTag+5, zType); return start_of_branch(rid, 0); } /* symbolic-name ":" date-time */ nTag = strlen(zTag); for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} if( zTag[i]==':' && fossil_isdate(&zTag[i+1]) ){ char *zDate = mprintf("%s", &zTag[i+1]); |
︙ | ︙ | |||
214 215 216 217 218 219 220 | if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){ Stmt q; char zUuid[UUID_SIZE+1]; memcpy(zUuid, zTag, nTag+1); canonical16(zUuid, nTag); rid = 0; if( zType[0]=='*' ){ | | | | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){ Stmt q; char zUuid[UUID_SIZE+1]; memcpy(zUuid, zTag, nTag+1); canonical16(zUuid, nTag); rid = 0; if( zType[0]=='*' ){ db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUuid); }else{ db_prepare(&q, "SELECT blob.rid" " FROM blob, event" " WHERE blob.uuid GLOB '%q*'" " AND event.objid=blob.rid" " AND event.type GLOB '%q'", zUuid, zType ); } if( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); |
︙ | ︙ | |||
243 244 245 246 247 248 249 | " FROM tag, tagxref, event" " WHERE tag.tagname='sym-%q' " " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " " AND event.objid=tagxref.rid " " AND event.type GLOB '%q'", zTag, zType ); | | > > > | | < | | | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | " FROM tag, tagxref, event" " WHERE tag.tagname='sym-%q' " " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " " AND event.objid=tagxref.rid " " AND event.type GLOB '%q'", zTag, zType ); if( rid>0 ){ if( startOfBranch ) rid = start_of_branch(rid,1); return rid; } /* Undocumented: numeric tags get translated directly into the RID */ if( memcmp(zTag, "rid:", 4)==0 ){ zTag += 4; for(i=0; fossil_isdigit(zTag[i]); i++){} if( zTag[i]==0 ){ if( strcmp(zType,"*")==0 ){ rid = atoi(zTag); }else{ rid = db_int(0, "SELECT event.objid" " FROM event" " WHERE event.objid=%s" " AND event.type GLOB '%q'", zTag /*safe-for-%s*/, zType); } } } return rid; } /* ** This routine takes a user-entered UUID which might be in mixed ** case and might only be a prefix of the full UUID and converts it ** into the full-length UUID in canonical form. ** ** If the input is not a UUID or a UUID prefix, then try to resolve ** the name as a tag. If multiple tags match, pick the latest. ** If the input name matches "tag:*" then always resolve as a tag. ** ** If the input is not a tag, then try to match it as an ISO-8601 date ** string YYYY-MM-DD HH:MM:SS and pick the nearest check-in to that date. ** If the input is of the form "date:*" then always resolve the name as ** a date. The forms "utc:*" and "local:" are deprecated. ** ** Return 0 on success. Return 1 if the name cannot be resolved. ** Return 2 name is ambiguous. */ int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ char *zName = blob_str(pName); int rid = symbolic_name_to_rid(zName, zType); |
︙ | ︙ | |||
308 309 310 311 312 313 314 | ** treat errors as fatal. zName must be a UUID, as described for ** name_to_uuid(). zType is also as described for that function. If ** zName does not resolve, 0 is returned. If it is ambiguous, a ** negative value is returned. On success the rid is returned and ** pUuid (if it is not NULL) is set to the a newly-allocated string, ** the full UUID, which must eventually be free()d by the caller. */ | | > > > > > > > > > > > > > > > > > > > > > > > > | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | ** treat errors as fatal. zName must be a UUID, as described for ** name_to_uuid(). zType is also as described for that function. If ** zName does not resolve, 0 is returned. If it is ambiguous, a ** negative value is returned. On success the rid is returned and ** pUuid (if it is not NULL) is set to the a newly-allocated string, ** the full UUID, which must eventually be free()d by the caller. */ int name_to_uuid2(const char *zName, const char *zType, char **pUuid){ int rid = symbolic_name_to_rid(zName, zType); if((rid>0) && pUuid){ *pUuid = db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid); } return rid; } /* ** name_collisions searches through events, blobs, and tickets for ** collisions of a given UUID based on its length on UUIDs no shorter ** than 4 characters in length. */ int name_collisions(const char *zName){ int c = 0; /* count of collisions for zName */ int nLen; /* length of zName */ nLen = strlen(zName); if( nLen>=4 && nLen<=UUID_SIZE && validate16(zName, nLen) ){ c = db_int(0, "SELECT" " (SELECT count(*) FROM ticket" " WHERE tkt_uuid GLOB '%q*') +" " (SELECT count(*) FROM tag" " WHERE tagname GLOB 'event-%q*') +" " (SELECT count(*) FROM blob" " WHERE uuid GLOB '%q*');", zName, zName, zName ); if( c<2 ) c = 0; } return c; } /* ** COMMAND: test-name-to-id ** ** Convert a name to a full artifact ID. */ void test_name_to_id(void){ |
︙ | ︙ | |||
361 362 363 364 365 366 367 | */ int name_to_typed_rid(const char *zName, const char *zType){ int rid; if( zName==0 || zName[0]==0 ) return 0; rid = symbolic_name_to_rid(zName, zType); if( rid<0 ){ | | < | < < < > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 | */ int name_to_typed_rid(const char *zName, const char *zType){ int rid; if( zName==0 || zName[0]==0 ) return 0; rid = symbolic_name_to_rid(zName, zType); if( rid<0 ){ fossil_fatal("ambiguous name: %s", zName); }else if( rid==0 ){ fossil_fatal("not found: %s", zName); } return rid; } int name_to_rid(const char *zName){ return name_to_typed_rid(zName, "*"); } /* ** WEBPAGE: ambiguous ** URL: /ambiguous?name=UUID&src=WEBPAGE ** ** The UUID given by the name parameter is ambiguous. Display a page ** that shows all possible choices and let the user select between them. */ void ambiguous_page(void){ Stmt q; const char *zName = P("name"); const char *zSrc = P("src"); char *z; if( zName==0 || zName[0]==0 || zSrc==0 || zSrc[0]==0 ){ fossil_redirect_home(); } style_header("Ambiguous Artifact ID"); @ <p>The artifact id <b>%h(zName)</b> is ambiguous and might @ mean any of the following: @ <ol> z = mprintf("%s", zName); canonical16(z, strlen(z)); db_prepare(&q, "SELECT uuid, rid FROM blob WHERE uuid GLOB '%q*'", z); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); int rid = db_column_int(&q, 1); @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)"> @ %s(zUuid)</a> - object_description(rid, 0, 0); @ </p></li> } db_finalize(&q); db_prepare(&q, " SELECT tkt_rid, tkt_uuid, title" " FROM ticket, ticketchng" " WHERE ticket.tkt_id = ticketchng.tkt_id" " AND tkt_uuid GLOB '%q*'" " GROUP BY tkt_uuid" " ORDER BY tkt_ctime DESC", z); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char *zUuid = db_column_text(&q, 1); const char *zTitle = db_column_text(&q, 2); @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)"> @ %s(zUuid)</a> - @ <ul></ul> @ Ticket hyperlink_to_uuid(zUuid); @ - %s(zTitle). @ <ul><li> object_description(rid, 0, 0); @ </li></ul> @ </p></li> } db_finalize(&q); db_prepare(&q, "SELECT rid, uuid FROM" " (SELECT tagxref.rid AS rid, substr(tagname, 7) AS uuid" " FROM tagxref, tag WHERE tagxref.tagid = tag.tagid" " AND tagname GLOB 'event-%q*') GROUP BY uuid", z); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); const char* zUuid = db_column_text(&q, 1); @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)"> @ %s(zUuid)</a> - @ <ul><li> object_description(rid, 0, 0); @ </li></ul> @ </p></li> } @ </ol> db_finalize(&q); style_footer(); } /* ** Convert the name in CGI parameter zParamName into a rid and return that ** rid. If the CGI parameter is missing or is not a valid artifact tag, ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
︙ | ︙ | |||
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | rid = symbolic_name_to_rid(zName, "*"); if( rid<0 ){ cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); rid = 0; } return rid; } /* ** COMMAND: whatis* ** Usage: %fossil whatis NAME ** ** Resolve the symbol NAME into its canonical 40-character SHA1-hash ** artifact name and provide a description of what role that artifact ** plays. */ void whatis_cmd(void){ int rid; const char *zName; int verboseFlag; db_find_and_open_repository(0,0); verboseFlag = find_option("verbose","v",0)!=0; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | | > > | > > > > > > > > > | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > > > > > > > > > > > | > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | > > > | | | > > | > > > | > > > > > > > > > > > | > > > > > > | < | | > > > > > > > > > > > > > > | > > > > > > > > > > | > > > | < > > > > > | | < > > > | > > > > > > | | > > > > | < < < < | > | > > > > > > > > > > | | > > > > > > > > > > > | > > > | > > > > > > > | > > | > > > > > > | > | | | < | > | | | > > > | < | < < | | < | > | > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 | rid = symbolic_name_to_rid(zName, "*"); if( rid<0 ){ cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath); rid = 0; } return rid; } /* ** Generate a description of artifact "rid" */ void whatis_rid(int rid, int verboseFlag){ Stmt q; int cnt; /* Basic information about the object. */ db_prepare(&q, "SELECT uuid, size, datetime(mtime%s), ipaddr" " FROM blob, rcvfrom" " WHERE rid=%d" " AND rcvfrom.rcvid=blob.rcvid", timeline_utc(), rid); if( db_step(&q)==SQLITE_ROW ){ if( verboseFlag ){ fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid); fossil_print("size: %d bytes\n", db_column_int(&q,1)); fossil_print("received: %s from %s\n", db_column_text(&q, 2), db_column_text(&q, 3)); }else{ fossil_print("artifact: %s\n", db_column_text(&q,0)); fossil_print("size: %d bytes\n", db_column_int(&q,1)); } } db_finalize(&q); /* Report any symbolic tags on this artifact */ db_prepare(&q, "SELECT substr(tagname,5)" " FROM tag JOIN tagxref ON tag.tagid=tagxref.tagid" " WHERE tagxref.rid=%d" " AND tagname GLOB 'sym-*'" " ORDER BY 1", rid ); cnt = 0; while( db_step(&q)==SQLITE_ROW ){ const char *zPrefix = cnt++ ? ", " : "tags: "; fossil_print("%s%s", zPrefix, db_column_text(&q,0)); } if( cnt ) fossil_print("\n"); db_finalize(&q); /* Report any HIDDEN, PRIVATE, CLUSTER, or CLOSED tags on this artifact */ db_prepare(&q, "SELECT tagname" " FROM tag JOIN tagxref ON tag.tagid=tagxref.tagid" " WHERE tagxref.rid=%d" " AND tag.tagid IN (5,6,7,9)" " ORDER BY 1", rid ); cnt = 0; while( db_step(&q)==SQLITE_ROW ){ const char *zPrefix = cnt++ ? ", " : "raw-tags: "; fossil_print("%s%s", zPrefix, db_column_text(&q,0)); } if( cnt ) fossil_print("\n"); db_finalize(&q); /* Check for entries on the timeline that reference this object */ db_prepare(&q, "SELECT type, datetime(mtime%s)," " coalesce(euser,user), coalesce(ecomment,comment)" " FROM event WHERE objid=%d", timeline_utc(), rid); if( db_step(&q)==SQLITE_ROW ){ const char *zType; switch( db_column_text(&q,0)[0] ){ case 'c': zType = "Check-in"; break; case 'w': zType = "Wiki-edit"; break; case 'e': zType = "Event"; break; case 't': zType = "Ticket-change"; break; case 'g': zType = "Tag-change"; break; default: zType = "Unknown"; break; } fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2), db_column_text(&q, 1)); fossil_print("comment: "); comment_print(db_column_text(&q,3), 0, 12, -1, g.comFmtFlags); } db_finalize(&q); /* Check to see if this object is used as a file in a check-in */ db_prepare(&q, "SELECT filename.name, blob.uuid, datetime(event.mtime%s)," " coalesce(euser,user), coalesce(ecomment,comment)" " FROM mlink, filename, blob, event" " WHERE mlink.fid=%d" " AND filename.fnid=mlink.fnid" " AND event.objid=mlink.mid" " AND blob.rid=mlink.mid" " ORDER BY event.mtime DESC /*sort*/", timeline_utc(), rid); while( db_step(&q)==SQLITE_ROW ){ fossil_print("file: %s\n", db_column_text(&q,0)); fossil_print(" part of [%S] by %s on %s\n", db_column_text(&q, 1), db_column_text(&q, 3), db_column_text(&q, 2)); fossil_print(" "); comment_print(db_column_text(&q,4), 0, 12, -1, g.comFmtFlags); } db_finalize(&q); /* Check to see if this object is used as an attachment */ db_prepare(&q, "SELECT attachment.filename," " attachment.comment," " attachment.user," " datetime(attachment.mtime%s)," " attachment.target," " CASE WHEN EXISTS(SELECT 1 FROM tag WHERE tagname=('tkt-'||target))" " THEN 'ticket'" " WHEN EXISTS(SELECT 1 FROM tag WHERE tagname=('wiki-'||target))" " THEN 'wiki' END," " attachment.attachid," " (SELECT uuid FROM blob WHERE rid=attachid)" " FROM attachment JOIN blob ON attachment.src=blob.uuid" " WHERE blob.rid=%d", timeline_utc(), rid ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("attachment: %s\n", db_column_text(&q,0)); fossil_print(" attached to %s %s\n", db_column_text(&q,5), db_column_text(&q,4)); if( verboseFlag ){ fossil_print(" via %s (%d)\n", db_column_text(&q,7), db_column_int(&q,6)); }else{ fossil_print(" via %s\n", db_column_text(&q,7)); } fossil_print(" by user %s on %s\n", db_column_text(&q,2), db_column_text(&q,3)); fossil_print(" "); comment_print(db_column_text(&q,1), 0, 12, -1, g.comFmtFlags); } db_finalize(&q); } /* ** COMMAND: whatis* ** Usage: %fossil whatis NAME ** ** Resolve the symbol NAME into its canonical 40-character SHA1-hash ** artifact name and provide a description of what role that artifact ** plays. */ void whatis_cmd(void){ int rid; const char *zName; int verboseFlag; int i; const char *zType = 0; db_find_and_open_repository(0,0); verboseFlag = find_option("verbose","v",0)!=0; zType = find_option("type",0,1); /* We should be done with options.. */ verify_all_options(); if( g.argc<3 ) usage("whatis NAME ..."); for(i=2; i<g.argc; i++){ zName = g.argv[i]; if( i>2 ) fossil_print("%.79c\n",'-'); rid = symbolic_name_to_rid(zName, zType); if( rid<0 ){ Stmt q; int cnt = 0; fossil_print("name: %s (ambiguous)\n", zName); db_prepare(&q, "SELECT rid FROM blob WHERE uuid>=lower(%Q) AND uuid<(lower(%Q)||'z')", zName, zName ); while( db_step(&q)==SQLITE_ROW ){ if( cnt++ ) fossil_print("%12s---- meaning #%d ----\n", " ", cnt); whatis_rid(db_column_int(&q, 0), verboseFlag); } db_finalize(&q); }else if( rid==0 ){ /* 0123456789 12 */ fossil_print("unknown: %s\n", zName); }else{ fossil_print("name: %s\n", zName); whatis_rid(rid, verboseFlag); } } } /* ** COMMAND: test-whatis-all ** Usage: %fossil test-whatis-all ** ** Show "whatis" information about every artifact in the repository */ void test_whatis_all_cmd(void){ Stmt q; int cnt = 0; db_find_and_open_repository(0,0); db_prepare(&q, "SELECT rid FROM blob ORDER BY rid"); while( db_step(&q)==SQLITE_ROW ){ if( cnt++ ) fossil_print("%.79c\n", '-'); whatis_rid(db_column_int(&q,0), 1); } db_finalize(&q); } /* ** COMMAND: test-ambiguous ** Usage: %fossil test-ambiguous [--minsize N] ** ** Show a list of ambiguous SHA1-hash abbreviations of N characters or ** more where N defaults to 4. Change N to a different value using ** the "--minsize N" command-line option. */ void test_ambiguous_cmd(void){ Stmt q, ins; int i; int minSize = 4; const char *zMinsize; char zPrev[100]; db_find_and_open_repository(0,0); zMinsize = find_option("minsize",0,1); if( zMinsize && atoi(zMinsize)>0 ) minSize = atoi(zMinsize); db_multi_exec("CREATE TEMP TABLE dups(uuid, cnt)"); db_prepare(&ins,"INSERT INTO dups(uuid) VALUES(substr(:uuid,1,:cnt))"); db_prepare(&q, "SELECT uuid FROM blob " "UNION " "SELECT substr(tagname,7) FROM tag WHERE tagname GLOB 'event-*' " "UNION " "SELECT tkt_uuid FROM ticket " "ORDER BY 1" ); zPrev[0] = 0; while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); for(i=0; zUuid[i]==zPrev[i] && zUuid[i]!=0; i++){} if( i>=minSize ){ db_bind_int(&ins, ":cnt", i); db_bind_text(&ins, ":uuid", zUuid); db_step(&ins); db_reset(&ins); } sqlite3_snprintf(sizeof(zPrev), zPrev, "%s", zUuid); } db_finalize(&ins); db_finalize(&q); db_prepare(&q, "SELECT uuid FROM dups ORDER BY length(uuid) DESC, uuid"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%s\n", db_column_text(&q, 0)); } db_finalize(&q); } /* ** Schema for the description table */ static const char zDescTab[] = @ CREATE TEMP TABLE IF NOT EXISTS description( @ rid INTEGER PRIMARY KEY, -- RID of the object @ uuid TEXT, -- SHA1 hash of the object @ ctime DATETIME, -- Time of creation @ isPrivate BOOLEAN DEFAULT 0, -- True for unpublished artifacts @ type TEXT, -- file, checkin, wiki, ticket, etc. @ summary TEXT, -- Summary comment for the object @ detail TEXT -- File name, check-in comment, etc @ ); ; /* ** Create the description table if it does not already exists. ** Populate fields of this table with descriptions for all artifacts ** whose RID matches the SQL expression in zWhere. */ void describe_artifacts(const char *zWhere){ db_multi_exec("%s", zDescTab/*safe-for-%s*/); /* Describe check-ins */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, event.mtime, 'checkin',\n" " 'check-in on ' || strftime('%%Y-%%m-%%d %%H:%%M',event.mtime)\n" " FROM event, blob\n" " WHERE (event.objid %s) AND event.type='ci'\n" " AND event.objid=blob.rid;", zWhere /*safe-for-%s*/ ); /* Describe files */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, event.mtime, 'file', 'file '||filename.name\n" " FROM mlink, blob, event, filename\n" " WHERE (mlink.fid %s)\n" " AND mlink.mid=event.objid\n" " AND filename.fnid=mlink.fnid\n" " AND mlink.fid=blob.rid;", zWhere /*safe-for-%s*/ ); /* Describe tags */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, tagxref.mtime, 'tag',\n" " 'tag '||substr((SELECT uuid FROM blob WHERE rid=tagxref.rid),1,16)\n" " FROM tagxref, blob\n" " WHERE (tagxref.srcid %s) AND tagxref.srcid!=tagxref.rid\n" " AND tagxref.srcid=blob.rid;", zWhere /*safe-for-%s*/ ); /* Cluster artifacts */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, tagxref.mtime, 'cluster', 'cluster'\n" " FROM tagxref, blob\n" " WHERE (tagxref.rid %s)\n" " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n" " AND blob.rid=tagxref.rid;", zWhere /*safe-for-%s*/ ); /* Ticket change artifacts */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, tagxref.mtime, 'ticket',\n" " 'ticket '||substr(tag.tagname,5,21)\n" " FROM tagxref, tag, blob\n" " WHERE (tagxref.rid %s)\n" " AND tag.tagid=tagxref.tagid\n" " AND tag.tagname GLOB 'tkt-*'" " AND blob.rid=tagxref.rid;", zWhere /*safe-for-%s*/ ); /* Wiki edit artifacts */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, tagxref.mtime, 'wiki',\n" " printf('wiki \"%%s\"',substr(tag.tagname,6))\n" " FROM tagxref, tag, blob\n" " WHERE (tagxref.rid %s)\n" " AND tag.tagid=tagxref.tagid\n" " AND tag.tagname GLOB 'wiki-*'" " AND blob.rid=tagxref.rid;", zWhere /*safe-for-%s*/ ); /* Event edit artifacts */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, tagxref.mtime, 'event',\n" " 'event '||substr(tag.tagname,7)\n" " FROM tagxref, tag, blob\n" " WHERE (tagxref.rid %s)\n" " AND tag.tagid=tagxref.tagid\n" " AND tag.tagname GLOB 'event-*'" " AND blob.rid=tagxref.rid;", zWhere /*safe-for-%s*/ ); /* Attachments */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, attachment.mtime, 'attach-control',\n" " 'attachment-control for '||attachment.filename\n" " FROM attachment, blob\n" " WHERE (attachment.attachid %s)\n" " AND blob.rid=attachment.attachid", zWhere /*safe-for-%s*/ ); db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n" "SELECT blob.rid, blob.uuid, attachment.mtime, 'attachment',\n" " 'attachment '||attachment.filename\n" " FROM attachment, blob\n" " WHERE (blob.rid %s)\n" " AND blob.rid NOT IN (SELECT rid FROM description)\n" " AND blob.uuid=attachment.src", zWhere /*safe-for-%s*/ ); /* Everything else */ db_multi_exec( "INSERT OR IGNORE INTO description(rid,uuid,type,summary)\n" "SELECT blob.rid, blob.uuid," " CASE WHEN blob.size<0 THEN 'phantom' ELSE '' END,\n" " 'unknown'\n" " FROM blob WHERE (blob.rid %s);", zWhere /*safe-for-%s*/ ); /* Mark private elements */ db_multi_exec( "UPDATE description SET isPrivate=1 WHERE rid IN private" ); } /* ** Print the content of the description table on stdout */ int describe_artifacts_to_stdout(const char *zWhere, const char *zLabel){ Stmt q; int cnt = 0; describe_artifacts(zWhere); db_prepare(&q, "SELECT uuid, summary, isPrivate\n" " FROM description\n" " ORDER BY ctime, type;" ); while( db_step(&q)==SQLITE_ROW ){ if( zLabel ){ fossil_print("%s\n", zLabel); zLabel = 0; } fossil_print(" %.16s %s", db_column_text(&q,0), db_column_text(&q,1)); if( db_column_int(&q,2) ) fossil_print(" (unpublished)"); fossil_print("\n"); cnt++; } db_finalize(&q); db_multi_exec("DELETE FROM description;"); return cnt; } /* ** COMMAND: test-describe-artifacts ** ** Usage: %fossil test-describe-artifacts [--from S] [--count N] ** ** Display a one-line description of every artifact. */ void test_describe_artifacts_cmd(void){ int iFrom = 0; int iCnt = 1000000; const char *z; char *zRange; db_find_and_open_repository(0,0); z = find_option("from",0,1); if( z ) iFrom = atoi(z); z = find_option("count",0,1); if( z ) iCnt = atoi(z); zRange = mprintf("BETWEEN %d AND %d", iFrom, iFrom+iCnt-1); describe_artifacts_to_stdout(zRange, 0); } /* ** WEBPAGE: bloblist ** ** Return a page showing all artifacts in the repository. Query parameters: ** ** n=N Show N artifacts ** s=S Start with artifact number S */ void bloblist_page(void){ Stmt q; int s = atoi(PD("s","0")); int n = atoi(PD("n","5000")); int mx = db_int(0, "SELECT max(rid) FROM blob"); char *zRange; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("List Of Artifacts"); if( mx>n && P("s")==0 ){ int i; @ <p>Select a range of artifacts to view:</p> @ <ul> for(i=1; i<=mx; i+=n){ @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n)) @ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a> } @ </ul> style_footer(); return; } if( mx>n ){ style_submenu_element("Index", "Index", "bloblist"); } zRange = mprintf("BETWEEN %d AND %d", s, s+n-1); describe_artifacts(zRange); db_prepare(&q, "SELECT rid, uuid, summary, isPrivate FROM description ORDER BY rid" ); @ <table cellpadding="0" cellspacing="0"> while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q,0); const char *zUuid = db_column_text(&q, 1); const char *zDesc = db_column_text(&q, 2); int isPriv = db_column_int(&q,2); @ <tr><td align="right">%d(rid)</td> @ <td> %z(href("%R/info/%!S",zUuid))%s(zUuid)</a> </td> @ <td align="left">%h(zDesc)</td> if( isPriv ){ @ <td>(unpublished)</td> } @ </tr> } @ </table> db_finalize(&q); style_footer(); } /* ** COMMAND: test-unsent ** ** Usage: %fossil test-unsent ** ** Show all artifacts in the unsent table */ void test_unsent_cmd(void){ db_find_and_open_repository(0,0); describe_artifacts_to_stdout("IN unsent", 0); } /* ** COMMAND: test-unclustered ** ** Usage: %fossil test-unclustered ** ** Show all artifacts in the unclustered table */ void test_unclusterd_cmd(void){ db_find_and_open_repository(0,0); describe_artifacts_to_stdout("IN unclustered", 0); } /* ** COMMAND: test-phantoms ** ** Usage: %fossil test-phantoms ** ** Show all phantom artifacts */ void test_phatoms_cmd(void){ db_find_and_open_repository(0,0); describe_artifacts_to_stdout("IN (SELECT rid FROM blob WHERE size<0)", 0); } /* Maximum number of collision examples to remember */ #define MAX_COLLIDE 25 /* ** WEBPAGE: hash-collisions ** ** Show the number of hash collisions for hash prefixes of various lengths. */ void hash_collisions_webpage(void){ int i, j, kk; int nHash = 0; Stmt q; char zPrev[UUID_SIZE+1]; struct { int cnt; char *azHit[MAX_COLLIDE]; char z[UUID_SIZE+1]; } aCollide[UUID_SIZE+1]; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } memset(aCollide, 0, sizeof(aCollide)); memset(zPrev, 0, sizeof(zPrev)); db_prepare(&q,"SELECT uuid FROM blob ORDER BY 1"); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q,0); int n = db_column_bytes(&q,0); int i; nHash++; for(i=0; zPrev[i] && zPrev[i]==zUuid[i]; i++){} if( i>0 && i<=UUID_SIZE ){ if( i>=4 && aCollide[i].cnt<MAX_COLLIDE ){ aCollide[i].azHit[aCollide[i].cnt] = mprintf("%.*s", i, zPrev); } aCollide[i].cnt++; if( aCollide[i].z[0]==0 ) memcpy(aCollide[i].z, zPrev, n+1); } memcpy(zPrev, zUuid, n+1); } db_finalize(&q); style_header("SHA1 Prefix Collisions"); style_submenu_element("Activity Reports", 0, "reports"); style_submenu_element("Stats", 0, "stat"); @ <table border=1><thead> @ <tr><th>Length<th>Instances<th>First Instance</tr> @ </thead><tbody> for(i=1; i<=UUID_SIZE; i++){ if( aCollide[i].cnt==0 ) continue; @ <tr><td>%d(i)<td>%d(aCollide[i].cnt)<td>%h(aCollide[i].z)</tr> } @ </tbody></table> @ <p>Total number of hashes: %d(nHash)</p> kk = 0; for(i=UUID_SIZE; i>=4; i--){ if( aCollide[i].cnt==0 ) continue; if( aCollide[i].cnt>200 ) break; kk += aCollide[i].cnt; if( aCollide[i].cnt<25 ){ @ <p>Collisions of length %d(i): }else{ @ <p>First 25 collisions of length %d(i): } for(j=0; j<aCollide[i].cnt && j<MAX_COLLIDE; j++){ char *zId = aCollide[i].azHit[j]; if( zId==0 ) continue; @ %z(href("%R/whatis/%s",zId))%h(zId)</a> } } style_footer(); } |
Changes to src/path.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | ** ** Author contact information: ** drh@sqlite.org ** ******************************************************************************* ** ** This file contains code used to trace paths of through the | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ** ** Author contact information: ** drh@sqlite.org ** ******************************************************************************* ** ** This file contains code used to trace paths of through the ** directed acyclic graph (DAG) of check-ins. */ #include "config.h" #include "path.h" #include <assert.h> #if INTERFACE /* Nodes for the paths through the DAG. |
︙ | ︙ | |||
110 111 112 113 114 115 116 | /* ** Compute the shortest path from iFrom to iTo ** ** If directOnly is true, then use only the "primary" links from parent to ** child. In other words, ignore merges. ** | | | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | /* ** Compute the shortest path from iFrom to iTo ** ** If directOnly is true, then use only the "primary" links from parent to ** child. In other words, ignore merges. ** ** Return a pointer to the beginning of the path (the iFrom node). ** Elements of the path can be traversed by following the PathNode.u.pTo ** pointer chain. ** ** Return NULL if no path is found. */ PathNode *path_shortest( int iFrom, /* Path starts here */ |
︙ | ︙ | |||
133 134 135 136 137 138 139 | path_reset(); path.pStart = path_new_node(iFrom, 0, 0); if( iTo==iFrom ){ path.pEnd = path.pStart; return path.pStart; } if( oneWayOnly && directOnly ){ | | | | | | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | path_reset(); path.pStart = path_new_node(iFrom, 0, 0); if( iTo==iFrom ){ path.pEnd = path.pStart; return path.pStart; } if( oneWayOnly && directOnly ){ db_prepare(&s, "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim" ); }else if( oneWayOnly ){ db_prepare(&s, "SELECT cid, 1 FROM plink WHERE pid=:pid " ); }else if( directOnly ){ db_prepare(&s, "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim " "UNION ALL " "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim" ); }else{ db_prepare(&s, "SELECT cid, 1 FROM plink WHERE pid=:pid " "UNION ALL " "SELECT pid, 0 FROM plink WHERE cid=:pid" ); } while( path.pCurrent ){ path.nStep++; |
︙ | ︙ | |||
197 198 199 200 201 202 203 | } /* ** COMMAND: test-shortest-path ** ** Usage: %fossil test-shortest-path ?--no-merge? VERSION1 VERSION2 ** | | | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | } /* ** COMMAND: test-shortest-path ** ** Usage: %fossil test-shortest-path ?--no-merge? VERSION1 VERSION2 ** ** Report the shortest path between two check-ins. If the --no-merge flag ** is used, follow only direct parent-child paths and omit merge links. */ void shortest_path_test_cmd(void){ int iFrom; int iTo; PathNode *p; int n; |
︙ | ︙ | |||
228 229 230 231 232 233 234 | "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)" " FROM blob, event" " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'", p->rid, p->rid); fossil_print("%4d: %5d %s", n, p->rid, z); fossil_free(z); if( p->u.pTo ){ | | | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)" " FROM blob, event" " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'", p->rid, p->rid); fossil_print("%4d: %5d %s", n, p->rid, z); fossil_free(z); if( p->u.pTo ){ fossil_print(" is a %s of\n", p->u.pTo->fromIsParent ? "parent" : "child"); }else{ fossil_print("\n"); } } } |
︙ | ︙ | |||
347 348 349 350 351 352 353 | int origName; /* Original name of file */ int curName; /* Current name of the file */ int newName; /* Name of file in next version */ NameChange *pNext; /* List of all name changes */ }; /* | | | | | 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | int origName; /* Original name of file */ int curName; /* Current name of the file */ int newName; /* Name of file in next version */ NameChange *pNext; /* List of all name changes */ }; /* ** Compute all file name changes that occur going from check-in iFrom ** to check-in iTo. ** ** The number of name changes is written into *pnChng. For each name ** change, two integers are allocated for *piChng. The first is the ** filename.fnid for the original name as seen in check-in iFrom and ** the second is for new name as it is used in check-in iTo. ** ** Space to hold *piChng is obtained from fossil_malloc() and should ** be released by the caller. ** ** This routine really has nothing to do with path. It is located |
︙ | ︙ | |||
380 381 382 383 384 385 386 387 388 389 390 391 392 393 | int nChng = 0; /* Number of files whose names have changed */ int *aChng; /* Two integers per name change */ int i; /* Loop counter */ Stmt q1; /* Query of name changes */ *pnChng = 0; *aiChng = 0; if( iFrom==iTo ) return; path_reset(); p = path_shortest(iFrom, iTo, 1, revOk==0); if( p==0 ) return; path_reverse_path(); db_prepare(&q1, "SELECT pfnid, fnid FROM mlink" | > > > > > | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 | int nChng = 0; /* Number of files whose names have changed */ int *aChng; /* Two integers per name change */ int i; /* Loop counter */ Stmt q1; /* Query of name changes */ *pnChng = 0; *aiChng = 0; if(0==iFrom){ fossil_fatal("Invalid 'from' RID: 0"); }else if(0==iTo){ fossil_fatal("Invalid 'to' RID: 0"); } if( iFrom==iTo ) return; path_reset(); p = path_shortest(iFrom, iTo, 1, revOk==0); if( p==0 ) return; path_reverse_path(); db_prepare(&q1, "SELECT pfnid, fnid FROM mlink" |
︙ | ︙ | |||
507 508 509 510 511 512 513 | fossil_free(zTo); } fossil_free(aChng); g.argv += 2; g.argc -= 2; } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 | fossil_free(zTo); } fossil_free(aChng); g.argv += 2; g.argc -= 2; } } /* Query to extract all rename operations */ static const char zRenameQuery[] = @ SELECT @ datetime(event.mtime), @ F.name AS old_name, @ T.name AS new_name, @ blob.uuid @ FROM mlink, filename F, filename T, event, blob @ WHERE coalesce(mlink.pfnid,0)!=0 AND mlink.pfnid!=mlink.fnid @ AND F.fnid=mlink.pfnid @ AND T.fnid=mlink.fnid @ AND event.objid=mlink.mid @ AND event.type='ci' @ AND blob.rid=mlink.mid @ ORDER BY 1 DESC, 2; ; /* ** WEBPAGE: test-rename-list ** ** Print a list of all file rename operations throughout history. ** This page is intended for for testing purposes only and may change ** or be discontinued without notice. */ void test_rename_list_page(void){ Stmt q; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("List Of File Name Changes"); @ <h3>NB: Experimental Page</h3> @ <table border="1" width="100%%"> @ <tr><th>Date & Time</th> @ <th>Old Name</th> @ <th>New Name</th> @ <th>Check-in</th></tr> db_prepare(&q, "%s", zRenameQuery/*safe-for-%s*/); while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zOld = db_column_text(&q, 1); const char *zNew = db_column_text(&q, 2); const char *zUuid = db_column_text(&q, 3); @ <tr> @ <td>%z(href("%R/timeline?c=%t",zDate))%s(zDate)</a></td> @ <td>%z(href("%R/finfo?name=%t",zOld))%h(zOld)</a></td> @ <td>%z(href("%R/finfo?name=%t",zNew))%h(zNew)</a></td> @ <td>%z(href("%R/info/%!S",zUuid))%S(zUuid)</a></td></tr> } @ </table> db_finalize(&q); style_footer(); } |
Changes to src/popen.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include <fcntl.h> /* ** Print a fatal error and quit. */ static void win32_fatal_error(const char *zMsg){ fossil_fatal("%s", zMsg); } #endif /* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. | > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include <fcntl.h> /* ** Print a fatal error and quit. */ static void win32_fatal_error(const char *zMsg){ fossil_fatal("%s", zMsg); } #else #include <signal.h> #include <sys/wait.h> #endif /* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. |
︙ | ︙ | |||
169 170 171 172 173 174 175 176 177 178 179 180 181 182 | close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); *pChildPid = 0; return 1; } if( *pChildPid==0 ){ int fd; int nErr = 0; /* This is the child process */ close(0); fd = dup(pout[0]); if( fd!=0 ) nErr++; | > | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); *pChildPid = 0; return 1; } signal(SIGPIPE,SIG_IGN); if( *pChildPid==0 ){ int fd; int nErr = 0; /* This is the child process */ close(0); fd = dup(pout[0]); if( fd!=0 ) nErr++; |
︙ | ︙ | |||
198 199 200 201 202 203 204 | return 0; } #endif } /* ** Close the connection to a child process previously created using | | | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | return 0; } #endif } /* ** Close the connection to a child process previously created using ** popen2(). */ void pclose2(int fdIn, FILE *pOut, int childPid){ #ifdef _WIN32 /* Not implemented, yet */ close(fdIn); fclose(pOut); #else close(fdIn); fclose(pOut); while( waitpid(0, 0, WNOHANG)>0 ) {} #endif } |
Changes to src/printf.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #include "config.h" #include "printf.h" #if defined(_WIN32) # include <io.h> # include <fcntl.h> #endif #include <time.h> /* ** Conversion types fall into various categories as defined by the ** following enumeration. */ #define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */ #define etFLOAT 2 /* Floating point. %f */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | #include "config.h" #include "printf.h" #if defined(_WIN32) # include <io.h> # include <fcntl.h> #endif #include <time.h> /* Two custom conversions are used to show a prefix of SHA1 hashes: ** ** %!S Prefix of a length appropriate for URLs ** %S Prefix of a length appropriate for human display ** ** The following macros help determine those lengths. FOSSIL_HASH_DIGITS ** is the default number of digits to display to humans. This value can ** be overridden using the hash-digits setting. FOSSIL_HASH_DIGITS_URL ** is the minimum number of digits to be used in URLs. The number used ** will always be at least 6 more than the number used for human output, ** or 40 if the number of digits in human output is 34 or more. */ #ifndef FOSSIL_HASH_DIGITS # define FOSSIL_HASH_DIGITS 10 /* For %S (human display) */ #endif #ifndef FOSSIL_HASH_DIGITS_URL # define FOSSIL_HASH_DIGITS_URL 16 /* For %!S (embedded in URLs) */ #endif /* ** Return the number of SHA1 hash digits to display. The number is for ** human output if the bForUrl is false and is destined for a URL if ** bForUrl is false. */ static int hashDigits(int bForUrl){ static int nDigitHuman = 0; static int nDigitUrl = 0; if( nDigitHuman==0 ){ nDigitHuman = db_get_int("hash-digits", FOSSIL_HASH_DIGITS); if( nDigitHuman < 6 ) nDigitHuman = 6; if( nDigitHuman > 40 ) nDigitHuman = 40; nDigitUrl = nDigitHuman + 6; if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL; if( nDigitUrl > 40 ) nDigitUrl = 40; } return bForUrl ? nDigitUrl : nDigitHuman; } /* ** Conversion types fall into various categories as defined by the ** following enumeration. */ #define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */ #define etFLOAT 2 /* Floating point. %f */ |
︙ | ︙ | |||
42 43 44 45 46 47 48 | #define etERROR 10 /* Used to indicate no such conversion type */ /* The rest are extensions, not normally found in printf() */ #define etBLOB 11 /* Blob objects. %b */ #define etBLOBSQL 12 /* Blob objects quoted for SQL. %B */ #define etSQLESCAPE 13 /* Strings with '\'' doubled. %q */ #define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '', NULL pointers replaced by SQL NULL. %Q */ | > | | | | | | | | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | #define etERROR 10 /* Used to indicate no such conversion type */ /* The rest are extensions, not normally found in printf() */ #define etBLOB 11 /* Blob objects. %b */ #define etBLOBSQL 12 /* Blob objects quoted for SQL. %B */ #define etSQLESCAPE 13 /* Strings with '\'' doubled. %q */ #define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '', NULL pointers replaced by SQL NULL. %Q */ #define etSQLESCAPE3 15 /* Double '"' characters within an indentifier. %w */ #define etPOINTER 16 /* The %p conversion */ #define etHTMLIZE 17 /* Make text safe for HTML */ #define etHTTPIZE 18 /* Make text safe for HTTP. "/" encoded as %2f */ #define etURLIZE 19 /* Make text safe for HTTP. "/" not encoded */ #define etFOSSILIZE 20 /* The fossil header encoding format. */ #define etPATH 21 /* Path type */ #define etWIKISTR 22 /* Timeline comment text rendered from a char*: %W */ #define etSTRINGID 23 /* String with length limit for a UUID prefix: %S */ #define etROOT 24 /* String value of g.zTop: %R */ /* ** An "etByte" is an 8-bit unsigned value. */ typedef unsigned char etByte; |
︙ | ︙ | |||
94 95 96 97 98 99 100 | { 's', 0, 4, etSTRING, 0, 0 }, { 'g', 0, 1, etGENERIC, 30, 0 }, { 'z', 0, 6, etDYNSTRING, 0, 0 }, { 'q', 0, 4, etSQLESCAPE, 0, 0 }, { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, { 'b', 0, 2, etBLOB, 0, 0 }, { 'B', 0, 2, etBLOBSQL, 0, 0 }, | | > | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | { 's', 0, 4, etSTRING, 0, 0 }, { 'g', 0, 1, etGENERIC, 30, 0 }, { 'z', 0, 6, etDYNSTRING, 0, 0 }, { 'q', 0, 4, etSQLESCAPE, 0, 0 }, { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, { 'b', 0, 2, etBLOB, 0, 0 }, { 'B', 0, 2, etBLOBSQL, 0, 0 }, { 'W', 0, 2, etWIKISTR, 0, 0 }, { 'h', 0, 4, etHTMLIZE, 0, 0 }, { 'R', 0, 0, etROOT, 0, 0 }, { 't', 0, 4, etHTTPIZE, 0, 0 }, /* "/" -> "%2F" */ { 'T', 0, 4, etURLIZE, 0, 0 }, /* "/" unchanged */ { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, { 'F', 0, 4, etFOSSILIZE, 0, 0 }, { 'S', 0, 4, etSTRINGID, 0, 0 }, { 'c', 0, 0, etCHARX, 0, 0 }, { 'o', 8, 0, etRADIX, 0, 2 }, { 'u', 10, 0, etRADIX, 0, 0 }, { 'x', 16, 0, etRADIX, 16, 1 }, { 'X', 16, 0, etRADIX, 0, 4 }, |
︙ | ︙ | |||
164 165 166 167 168 169 170 | } /* ** Return an appropriate set of flags for wiki_convert() for displaying ** comments on a timeline. These flag settings are determined by ** configuration parameters. ** | | | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | } /* ** Return an appropriate set of flags for wiki_convert() for displaying ** comments on a timeline. These flag settings are determined by ** configuration parameters. ** ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2 ** flags) and is false for plain "%W". The ! indicates that the text is ** to be rendered on a form rather than the timeline and that block markup ** is acceptable even if the "timeline-block-markup" setting is false. */ static int wiki_convert_flags(int altForm2){ static int wikiFlags = 0; if( wikiFlags==0 ){ if( altForm2 || db_get_boolean("timeline-block-markup", 0) ){ |
︙ | ︙ | |||
606 607 608 609 610 611 612 | break; } case etROOT: { bufpt = g.zTop ? g.zTop : ""; length = (int)strlen(bufpt); break; } | | < < < > > | 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 | break; } case etROOT: { bufpt = g.zTop ? g.zTop : ""; length = (int)strlen(bufpt); break; } case etSTRINGID: case etSTRING: case etDYNSTRING: { int limit = flag_alternateform ? va_arg(ap,int) : -1; bufpt = va_arg(ap,char*); if( bufpt==0 ){ bufpt = ""; }else if( xtype==etDYNSTRING ){ zExtra = bufpt; }else if( xtype==etSTRINGID ){ precision = hashDigits(flag_altform2); } length = StrNLen32(bufpt, limit); if( precision>=0 && precision<length ) length = precision; break; } case etBLOB: { int limit = flag_alternateform ? va_arg(ap, int) : -1; |
︙ | ︙ | |||
655 656 657 658 659 660 661 | } bufpt[j++] = '\''; length = j; assert( length==n+cnt+2 ); break; } case etSQLESCAPE: | > | > | | | | | 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 | } bufpt[j++] = '\''; length = j; assert( length==n+cnt+2 ); break; } case etSQLESCAPE: case etSQLESCAPE2: case etSQLESCAPE3: { int i, j, n, ch, isnull; int needQuote; int limit = flag_alternateform ? va_arg(ap,int) : -1; char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote characters */ char *escarg = va_arg(ap,char*); isnull = escarg==0; if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); if( limit<0 ) limit = strlen(escarg); for(i=n=0; i<limit; i++){ if( escarg[i]==q ) n++; } needQuote = !isnull && xtype==etSQLESCAPE2; n += i + 1 + needQuote*2; if( n>etBUFSIZE ){ bufpt = zExtra = fossil_malloc( n ); }else{ bufpt = buf; } j = 0; if( needQuote ) bufpt[j++] = q; for(i=0; i<limit; i++){ bufpt[j++] = ch = escarg[i]; if( ch==q ) bufpt[j++] = ch; } if( needQuote ) bufpt[j++] = q; bufpt[j] = 0; length = j; if( precision>=0 && precision<length ) length = precision; break; } case etHTMLIZE: { int limit = flag_alternateform ? va_arg(ap,int) : -1; |
︙ | ︙ | |||
857 858 859 860 861 862 863 | #endif assert( toStdErr==0 || toStdErr==1 ); fwrite(z, 1, n, toStdErr ? stderr : stdout); fflush(toStdErr ? stderr : stdout); } /* | | | | > > > > | 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 | #endif assert( toStdErr==0 || toStdErr==1 ); fwrite(z, 1, n, toStdErr ? stderr : stdout); fflush(toStdErr ? stderr : stdout); } /* ** Force the standard output cursor to move to the beginning ** of a line, if it is not there already. */ int fossil_force_newline(void){ if( g.cgiOutput==0 && stdoutAtBOL==0 ){ fossil_puts("\n", 0); return 1; } return 0; } /* ** Indicate that the cursor has moved to the start of a line by means ** other than writing to standard output. */ void fossil_new_line_started(void){ |
︙ | ︙ | |||
916 917 918 919 920 921 922 | static void fossil_errorlog(const char *zFormat, ...){ struct tm *pNow; time_t now; FILE *out; const char *z; int i; va_list ap; | | | > > > > | | 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 | static void fossil_errorlog(const char *zFormat, ...){ struct tm *pNow; time_t now; FILE *out; const char *z; int i; va_list ap; static const char *const azEnv[] = { "HTTP_HOST", "HTTP_USER_AGENT", "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_NAME" }; if( g.zErrlog==0 ) return; out = fossil_fopen(g.zErrlog, "a"); if( out==0 ) return; now = time(0); pNow = gmtime(&now); fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n", pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday+1, pNow->tm_hour, pNow->tm_min, pNow->tm_sec); va_start(ap, zFormat); vfprintf(out, zFormat, ap); fprintf(out, "\n"); va_end(ap); for(i=0; i<sizeof(azEnv)/sizeof(azEnv[0]); i++){ char *p; if( (p = fossil_getenv(azEnv[i]))!=0 ){ fprintf(out, "%s=%s\n", azEnv[i], p); fossil_filename_free(p); }else if( (z = P(azEnv[i]))!=0 ){ fprintf(out, "%s=%s\n", azEnv[i], z); } } fclose(out); } /* |
︙ | ︙ |
Added src/publish.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to implement the "publish" and ** "unpublished" commands. */ #include "config.h" #include "publish.h" #include <assert.h> /* ** COMMAND: unpublished ** ** Usage: %fossil unpublished ?OPTIONS? ** ** Show a list of unpublished or "private" artifacts. Unpublished artifacts ** will never push and hence will not be shared with collaborators. ** ** By default, this command only shows unpublished check-ins. To show ** all unpublished artifacts, use the --all command-line option. ** ** OPTIONS: ** --all Show all artifacts, not just check-ins */ void unpublished_cmd(void){ int bAll = find_option("all",0,0)!=0; db_find_and_open_repository(0,0); verify_all_options(); if( bAll ){ describe_artifacts_to_stdout("IN private", 0); }else{ describe_artifacts_to_stdout( "IN (SELECT rid FROM private CROSS JOIN event" " WHERE private.rid=event.objid" " AND event.type='ci')", 0); } } /* ** COMMAND: publish ** ** Usage: %fossil publish ?--only? TAGS... ** ** Cause artifacts identified by TAGS... to be published (made non-private). ** This can be used (for example) to convert a private branch into a public ** branch, or to publish a bundle that was imported privately. ** ** If any of TAGS names a branch, then all check-ins on the most recent ** instance of that branch are included, not just the most recent check-in. ** ** If any of TAGS name check-ins then all files and tags associated with ** those check-ins are also published automatically. Except if the --only ** option is used, then only the specific artifacts identified by TAGS ** are published. ** ** If a TAG is already public, this command is a harmless no-op. */ void publish_cmd(void){ int bOnly = find_option("only",0,0)!=0; int bTest = find_option("test",0,0)!=0; /* Undocumented --test option */ int bExclusive = find_option("exclusive",0,0)!=0; /* undocumented */ int i; db_find_and_open_repository(0,0); verify_all_options(); if( g.argc<3 ) usage("?--only? TAGS..."); db_begin_transaction(); db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY);"); for(i=2; i<g.argc; i++){ int rid = name_to_rid(g.argv[i]); if( db_exists("SELECT 1 FROM tagxref" " WHERE rid=%d AND tagid=%d" " AND tagtype>0 AND value=%Q", rid,TAG_BRANCH,g.argv[i]) ){ rid = start_of_branch(rid, 1); compute_descendants(rid, 1000000000); }else{ db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", rid); } } if( !bOnly ){ find_checkin_associates("ok", bExclusive); } if( bTest ){ /* If the --test option is used, then do not actually publish any ** artifacts. Instead, just list the artifact information on standard ** output. The --test option is useful for verifying correct operation ** of the logic that figures out which artifacts to publish, such as ** the find_checkin_associates() routine */ describe_artifacts_to_stdout("IN ok", 0); }else{ /* Standard behavior is simply to remove the published documents from ** the PRIVATE table */ db_multi_exec( "DELETE FROM ok WHERE rid NOT IN private;" "DELETE FROM private WHERE rid IN ok;" "INSERT OR IGNORE INTO unsent SELECT rid FROM ok;" "INSERT OR IGNORE INTO unclustered SELECT rid FROM ok;" ); } db_end_transaction(0); } |
Added src/purge.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to implement the "purge" command and ** related functionality for removing check-ins from a repository. It also ** manages the graveyard of purged content. */ #include "config.h" #include "purge.h" #include <assert.h> /* ** SQL code used to initialize the schema of a bundle. ** ** The purgeevent table contains one entry for each purge event. For each ** purge event, multiple artifacts might have been removed. Each removed ** artifact is stored as an entry in the purgeitem table. ** ** The purgeevent and purgeitem tables are not synced, even by the ** "fossil config" command. They exist only as a backup in case of a ** mistaken purge or for content recovery in case there is a bug in the ** purge command. */ static const char zPurgeInit[] = @ CREATE TABLE IF NOT EXISTS "%w".purgeevent( @ peid INTEGER PRIMARY KEY, -- Unique ID for the purge event @ ctime DATETIME, -- When purge occurred. Seconds since 1970. @ pnotes TEXT -- Human-readable notes about the purge event @ ); @ CREATE TABLE IF NOT EXISTS "%w".purgeitem( @ piid INTEGER PRIMARY KEY, -- ID for the purge item @ peid INTEGER REFERENCES purgeevent ON DELETE CASCADE, -- Purge event @ orid INTEGER, -- Original RID before purged @ uuid TEXT NOT NULL, -- SHA1 hash of the purged artifact @ srcid INTEGER, -- Basis purgeitem for delta compression @ isPrivate BOOLEAN, -- True if artifact was originally private @ sz INT NOT NULL, -- Uncompressed size of the purged artifact @ desc TEXT, -- Brief description of this artifact @ data BLOB -- Compressed artifact content @ ); ; /* ** This routine purges multiple artifacts from the repository, transfering ** those artifacts into the PURGEITEM table. ** ** Prior to invoking this routine, the caller must create a (TEMP) table ** named zTab that contains the RID of every artifact to be purged. ** ** This routine does the following: ** ** (1) Create the purgeevent and purgeitem tables, if required ** (2) Create a new purgeevent ** (3) Make sure no DELTA table entries depend on purged artifacts ** (4) Create new purgeitem entries for each purged artifact ** (5) Remove purged artifacts from the BLOB table ** (6) Remove references to purged artifacts in the following tables: ** (a) EVENT ** (b) PRIVATE ** (c) MLINK ** (d) PLINK ** (e) LEAF ** (f) UNCLUSTERED ** (g) UNSENT ** (h) BACKLINK ** (i) ATTACHMENT ** (j) TICKETCHNG ** (7) If any ticket artifacts were removed (6j) then rebuild the ** corresponding ticket entries. Possibly remove entries from ** the ticket table. ** ** Stops 1-4 (saving the purged artifacts into the graveyard) are only ** undertaken if the moveToGraveyard flag is true. */ int purge_artifact_list( const char *zTab, /* TEMP table containing list of RIDS to be purged */ const char *zNote, /* Text of the purgeevent.pnotes field */ int moveToGraveyard /* Move purged artifacts into the graveyard */ ){ int peid = 0; /* New purgeevent ID */ Stmt q; /* General-use prepared statement */ char *z; assert( g.repositoryOpen ); /* Main database must already be open */ db_begin_transaction(); z = sqlite3_mprintf("IN \"%w\"", zTab); describe_artifacts(z); sqlite3_free(z); /* Make sure we are not removing a manifest that is the baseline of some ** manifest that is being left behind. This step is not strictly necessary. ** is is just a safety check. */ if( purge_baseline_out_from_under_delta(zTab) ){ fossil_fatal("attempt to purge a baseline manifest without also purging " "all of its deltas"); } /* Make sure that no delta that is left behind requires a purged artifact ** as its basis. If such artifacts exist, go ahead and undelta them now. */ db_prepare(&q, "SELECT rid FROM delta WHERE srcid IN \"%w\"" " AND rid NOT IN \"%w\"", zTab, zTab); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); content_undelta(rid); verify_before_commit(rid); } db_finalize(&q); /* Construct the graveyard and copy the artifacts to be purged into the ** graveyard */ if( moveToGraveyard ){ db_multi_exec(zPurgeInit /*works-like:"%w%w"*/, db_name("repository"), db_name("repository")); db_multi_exec( "INSERT INTO purgeevent(ctime,pnotes) VALUES(now(),%Q)", zNote ); peid = db_last_insert_rowid(); db_prepare(&q, "SELECT rid FROM delta WHERE rid IN \"%w\"" " AND srcid NOT IN \"%w\"", zTab, zTab); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); content_undelta(rid); } db_finalize(&q); db_multi_exec( "INSERT INTO purgeitem(peid,orid,uuid,sz,isPrivate,desc,data)" " SELECT %d, rid, uuid, size," " EXISTS(SELECT 1 FROM private WHERE private.rid=blob.rid)," " (SELECT summary FROM description WHERE rid=blob.rid)," " content" " FROM blob WHERE rid IN \"%w\"", peid, zTab ); db_multi_exec( "UPDATE purgeitem" " SET srcid=(SELECT piid FROM purgeitem px, delta" " WHERE px.orid=delta.srcid" " AND delta.rid=purgeitem.orid)" " WHERE peid=%d", peid ); } /* Remove the artifacts being purged. Also remove all references to those ** artifacts from the secondary tables. */ db_multi_exec("DELETE FROM blob WHERE rid IN \"%w\"", zTab); db_multi_exec("DELETE FROM delta WHERE rid IN \"%w\"", zTab); db_multi_exec("DELETE FROM delta WHERE srcid IN \"%w\"", zTab); db_multi_exec("DELETE FROM event WHERE objid IN \"%w\"", zTab); db_multi_exec("DELETE FROM private WHERE rid IN \"%w\"", zTab); db_multi_exec("DELETE FROM mlink WHERE mid IN \"%w\"", zTab); db_multi_exec("DELETE FROM plink WHERE pid IN \"%w\"", zTab); db_multi_exec("DELETE FROM plink WHERE cid IN \"%w\"", zTab); db_multi_exec("DELETE FROM leaf WHERE rid IN \"%w\"", zTab); db_multi_exec("DELETE FROM phantom WHERE rid IN \"%w\"", zTab); db_multi_exec("DELETE FROM unclustered WHERE rid IN \"%w\"", zTab); db_multi_exec("DELETE FROM unsent WHERE rid IN \"%w\"", zTab); db_multi_exec("DELETE FROM tagxref" " WHERE rid IN \"%w\"" " OR srcid IN \"%w\"" " OR origid IN \"%w\"", zTab, zTab, zTab); db_multi_exec("DELETE FROM backlink WHERE srctype=0 AND srcid IN \"%w\"", zTab); db_multi_exec( "CREATE TEMP TABLE \"%w_tickets\" AS" " SELECT DISTINCT tkt_uuid FROM ticket WHERE tkt_id IN" " (SELECT tkt_id FROM ticketchng WHERE tkt_rid IN \"%w\")", zTab, zTab); db_multi_exec("DELETE FROM ticketchng WHERE tkt_rid IN \"%w\"", zTab); db_prepare(&q, "SELECT tkt_uuid FROM \"%w_tickets\"", zTab); while( db_step(&q)==SQLITE_ROW ){ ticket_rebuild_entry(db_column_text(&q, 0)); } db_finalize(&q); /* db_multi_exec("DROP TABLE \"%w_tickets\"", zTab); */ /* Mission accomplished */ db_end_transaction(0); return peid; } /* ** The TEMP table named zTab contains RIDs for a set of check-ins. ** ** Check to see if any check-in in zTab is a baseline manifest for some ** delta manifest that is not in zTab. Return true if zTab contains a ** baseline for a delta that is not in zTab. ** ** This is a database integrity preservation check. The check-ins in zTab ** are about to be deleted or otherwise made inaccessible. This routine ** is checking to ensure that purging the check-ins in zTab will not delete ** a baseline manifest out from under a delta. */ int purge_baseline_out_from_under_delta(const char *zTab){ if( !db_table_has_column("repository","plink","baseid") ){ /* Skip this check if the current database is an older schema that ** does not contain the PLINK.BASEID field. */ return 0; }else{ return db_int(0, "SELECT 1 FROM plink WHERE baseid IN \"%w\" AND cid NOT IN \"%w\"", zTab, zTab); } } /* ** The TEMP table named zTab contains the RIDs for a set of check-in ** artifacts. Expand this set (by adding new entries to zTab) to include ** all other artifacts that are used the set of check-ins in ** the original list. ** ** If the bExclusive flag is true, then the set is only expanded by ** artifacts that are used exclusively by the check-ins in the set. ** When bExclusive is false, then all artifacts used by the check-ins ** are added even if those artifacts are also used by other check-ins ** not in the set. ** ** The "fossil publish" command with the (undocumented) --test and ** --exclusive options can be used for interactiving testing of this ** function. */ void find_checkin_associates(const char *zTab, int bExclusive){ db_begin_transaction(); /* Compute the set of files that need to be added to zTab */ db_multi_exec("CREATE TEMP TABLE \"%w_files\"(fid INTEGER PRIMARY KEY)",zTab); db_multi_exec( "INSERT OR IGNORE INTO \"%w_files\"(fid)" " SELECT fid FROM mlink WHERE fid!=0 AND mid IN \"%w\"", zTab, zTab ); if( bExclusive ){ /* But take out all files that are referenced by check-ins not in zTab */ db_multi_exec( "DELETE FROM \"%w_files\"" " WHERE fid IN (SELECT fid FROM mlink" " WHERE fid IN \"%w_files\"" " AND mid NOT IN \"%w\")", zTab, zTab, zTab ); } /* Compute the set of tags that need to be added to zTag */ db_multi_exec("CREATE TEMP TABLE \"%w_tags\"(tid INTEGER PRIMARY KEY)",zTab); db_multi_exec( "INSERT OR IGNORE INTO \"%w_tags\"(tid)" " SELECT DISTINCT srcid FROM tagxref WHERE rid in \"%w\" AND srcid!=0", zTab, zTab ); if( bExclusive ){ /* But take out tags that references some check-ins in zTab and other ** check-ins not in zTab. The current Fossil implementation never creates ** such tags, so the following should usually be a no-op. But the file ** format specification allows such tags, so we should check for them. */ db_multi_exec( "DELETE FROM \"%w_tags\"" " WHERE tid IN (SELECT srcid FROM tagxref" " WHERE srcid IN \"%w_tags\"" " AND rid NOT IN \"%w\")", zTab, zTab, zTab ); } /* Transfer the extra artifacts into zTab */ db_multi_exec( "INSERT OR IGNORE INTO \"%w\" SELECT fid FROM \"%w_files\";" "INSERT OR IGNORE INTO \"%w\" SELECT tid FROM \"%w_tags\";" "DROP TABLE \"%w_files\";" "DROP TABLE \"%w_tags\";", zTab, zTab, zTab, zTab, zTab, zTab ); db_end_transaction(0); } /* ** Display the content of a single purge event. */ static void purge_list_event_content(int peid){ Stmt q; sqlite3_int64 sz = 0; db_prepare(&q, "SELECT piid, substr(uuid,1,16), srcid, isPrivate," " length(data), desc" " FROM purgeitem WHERE peid=%d", peid); while( db_step(&q)==SQLITE_ROW ){ fossil_print(" %5d %s %4s %c %10d %s\n", db_column_int(&q,0), db_column_text(&q,1), db_column_text(&q,2), db_column_int(&q,3) ? 'P' : ' ', db_column_int(&q,4), db_column_text(&q,5)); sz += db_column_int(&q,4); } db_finalize(&q); fossil_print("%.11c%16s%.8c%10lld\n", ' ', "Total:", ' ', sz); } /* ** Extract the content for purgeitem number piid into a Blob. Return ** the number of errors. */ static int purge_extract_item( int piid, /* ID of the item to extract */ Blob *pOut /* Write the content into this blob */ ){ Stmt q; int srcid; Blob h1, h2, x; static Bag busy; db_prepare(&q, "SELECT uuid, srcid, data FROM purgeitem" " WHERE piid=%d", piid); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); fossil_fatal("missing purge-item %d", piid); } if( bag_find(&busy, piid) ) return 1; srcid = db_column_int(&q, 1); blob_zero(pOut); blob_zero(&x); db_column_blob(&q, 2, &x); blob_uncompress(&x, pOut); blob_reset(&x); if( srcid>0 ){ Blob baseline, out; bag_insert(&busy, piid); purge_extract_item(srcid, &baseline); blob_zero(&out); blob_delta_apply(&baseline, pOut, &out); blob_reset(pOut); *pOut = out; blob_reset(&baseline); } bag_remove(&busy, piid); blob_zero(&h1); db_column_blob(&q, 0, &h1); sha1sum_blob(pOut, &h2); if( blob_compare(&h1, &h2)!=0 ){ fossil_fatal("SHA1 hash mismatch - wanted %s, got %s", blob_str(&h1), blob_str(&h2)); } blob_reset(&h1); blob_reset(&h2); db_finalize(&q); return 0; } /* ** There is a TEMP table ix(piid,srcid) containing a set of purgeitems ** that need to be transferred to the BLOB table. This routine does ** all items that have srcid=iSrc. The pBasis blob holds the content ** of the source document if iSrc>0. */ static void purge_item_resurrect(int iSrc, Blob *pBasis){ Stmt q; static Bag busy; assert( pBasis!=0 || iSrc==0 ); if( iSrc>0 ){ if( bag_find(&busy, iSrc) ){ fossil_fatal("delta loop while uncompressing purged artifacts"); } bag_insert(&busy, iSrc); } db_prepare(&q, "SELECT uuid, data, isPrivate, ix.piid" " FROM ix, purgeitem" " WHERE ix.srcid=%d" " AND ix.piid=purgeitem.piid;", iSrc ); while( db_step(&q)==SQLITE_ROW ){ Blob h1, h2, c1, c2; int isPriv, rid; blob_zero(&h1); db_column_blob(&q, 0, &h1); blob_zero(&c1); db_column_blob(&q, 1, &c1); blob_uncompress(&c1, &c1); blob_zero(&c2); if( pBasis ){ blob_delta_apply(pBasis, &c1, &c2); blob_reset(&c1); }else{ c2 = c1; } sha1sum_blob(&c2, &h2); if( blob_compare(&h1, &h2)!=0 ){ fossil_fatal("SHA1 hash mismatch - wanted %s, got %s", blob_str(&h1), blob_str(&h2)); } blob_reset(&h2); isPriv = db_column_int(&q, 2); rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv); if( rid==0 ){ fossil_fatal("%s", g.zErrMsg); }else{ if( !isPriv ) content_make_public(rid); content_get(rid, &c1); manifest_crosslink(rid, &c1, MC_NO_ERRORS); } purge_item_resurrect(db_column_int(&q,3), &c2); blob_reset(&c2); } db_finalize(&q); if( iSrc>0 ) bag_remove(&busy, iSrc); } /* ** COMMAND: purge ** ** The purge command removes content from a repository and stores that content ** in a "graveyard". The graveyard exists so that content can be recovered ** using the "fossil purge undo" command. ** ** fossil purge cat UUID... ** ** Write the content of one or more artifacts in the graveyard onto ** standard output. ** ** fossil purge ?checkins? TAGS... ?OPTIONS? ** ** Move the check-ins identified by TAGS and all of their descendants ** out of the repository and into the graveyard. The "checkins" ** subcommand keyword is option and can be omitted as long as TAGS ** does not conflict with any other subcommand. ** ** If a TAGS includes a branch name then it means all the check-ins ** on the most recent occurrance of that branch. ** ** --explain Make no changes, but show what would happen. ** --dry-run Make no chances. ** ** fossil purge list|ls ?-l? ** ** Show the graveyard of prior purges. The -l option gives more ** detail in the output. ** ** fossil purge obliterate ID... ** ** Remove one or more purge events from the graveyard. Once a purge ** event is obliterated, it can no longer be undone. ** ** fossil purge undo ID ** ** Restore the content previously removed by purge ID. ** ** SUMMARY: ** fossil purge cat UUID... ** fossil purge [checkins] TAGS... [--explain] ** fossil purge list ** fossil purge obliterate ID... ** fossil purge undo ID */ void purge_cmd(void){ const char *zSubcmd; int n; Stmt q; if( g.argc<3 ) usage("SUBCOMMAND ?ARGS?"); zSubcmd = g.argv[2]; db_find_and_open_repository(0,0); n = (int)strlen(zSubcmd); if( strncmp(zSubcmd, "cat", n)==0 ){ int i, piid; Blob content; if( g.argc<4 ) usage("cat UUID..."); for(i=3; i<g.argc; i++){ piid = db_int(0, "SELECT piid FROM purgeitem WHERE uuid LIKE '%q%%'", g.argv[i]); if( piid==0 ) fossil_fatal("no such item: %s", g.argv[3]); purge_extract_item(piid, &content); blob_write_to_file(&content, "-"); blob_reset(&content); } /* The "checkins" subcommand goes here in alphabetical order, but it must ** be moved to the end since it is the default case */ }else if( strncmp(zSubcmd, "list", n)==0 || strcmp(zSubcmd,"ls")==0 ){ int showDetail = find_option("l","l",0)!=0; if( !db_table_exists("repository","purgeevent") ) return; db_prepare(&q, "SELECT peid, datetime(ctime,'unixepoch','localtime')" " FROM purgeevent"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%4d on %s\n", db_column_int(&q,0), db_column_text(&q,1)); if( showDetail ){ purge_list_event_content(db_column_int(&q,0)); } } db_finalize(&q); }else if( strncmp(zSubcmd, "obliterate", n)==0 ){ int i; if( g.argc<4 ) usage("obliterate ID..."); db_begin_transaction(); for(i=3; i<g.argc; i++){ int peid = atoi(g.argv[i]); if( !db_exists("SELECT 1 FROM purgeevent WHERE peid=%d",peid) ){ fossil_fatal("no such purge event: %s", g.argv[i]); } db_multi_exec( "DELETE FROM purgeevent WHERE peid=%d;" "DELETE FROM purgeitem WHERE peid=%d;", peid, peid ); } db_end_transaction(0); }else if( strncmp(zSubcmd, "undo", n)==0 ){ int peid; if( g.argc!=4 ) usage("undo ID"); peid = atoi(g.argv[3]); db_begin_transaction(); db_multi_exec( "CREATE TEMP TABLE ix(" " piid INTEGER PRIMARY KEY," " srcid INTEGER" ");" "CREATE INDEX ixsrcid ON ix(srcid);" "INSERT INTO ix(piid,srcid) " " SELECT piid, coalesce(srcid,0) FROM purgeitem WHERE peid=%d;", peid ); db_multi_exec( "DELETE FROM shun" " WHERE uuid IN (SELECT uuid FROM purgeitem WHERE peid=%d);", peid ); manifest_crosslink_begin(); purge_item_resurrect(0, 0); manifest_crosslink_end(0); db_multi_exec("DELETE FROM purgeevent WHERE peid=%d", peid); db_multi_exec("DELETE FROM purgeitem WHERE peid=%d", peid); db_end_transaction(0); }else{ /* The "checkins" command is the default and so must occur last */ int explainOnly = find_option("explain",0,0)!=0; int dryRun = find_option("dry-run",0,0)!=0; int i; int vid; int nCkin; int nArtifact; verify_all_options(); db_begin_transaction(); i = strncmp(zSubcmd,"checkins",n)==0 ? 3 : 2; if( i>=g.argc ) usage("[checkin] TAGS... [--explain]"); db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); for(; i<g.argc; i++){ int r = name_to_typed_rid(g.argv[i], "br"); compute_descendants(r, 1000000000); } vid = db_lget_int("checkout",0); if( db_exists("SELECT 1 FROM ok WHERE rid=%d",vid) ){ fossil_fatal("cannot purge the current checkout"); } nCkin = db_int(0, "SELECT count(*) FROM ok"); find_checkin_associates("ok", 1); nArtifact = db_int(0, "SELECT count(*) FROM ok"); if( explainOnly ){ describe_artifacts_to_stdout("IN ok", 0); }else{ int peid = purge_artifact_list("ok","",1); fossil_print("%d check-ins and %d artifacts purged.\n", nCkin, nArtifact); fossil_print("undoable using \"%s purge undo %d\".\n", g.nameOfExe, peid); } db_end_transaction(explainOnly||dryRun); } } |
Changes to src/rebuild.c.
︙ | ︙ | |||
45 46 47 48 49 50 51 | @ CREATE TABLE IF NOT EXISTS shun( @ uuid UNIQUE, -- UUID of artifact to be shunned. Canonical form @ mtime INTEGER, -- When added. Seconds since 1970 @ scom TEXT -- Optional text explaining why the shun occurred @ ); @ @ -- Artifacts that should not be pushed are stored in the "private" | | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | @ CREATE TABLE IF NOT EXISTS shun( @ uuid UNIQUE, -- UUID of artifact to be shunned. Canonical form @ mtime INTEGER, -- When added. Seconds since 1970 @ scom TEXT -- Optional text explaining why the shun occurred @ ); @ @ -- Artifacts that should not be pushed are stored in the "private" @ -- table. @ -- @ CREATE TABLE IF NOT EXISTS private(rid INTEGER PRIMARY KEY); @ @ -- Some ticket content (such as the originators email address or contact @ -- information) needs to be obscured to protect privacy. This is achieved @ -- by storing an SHA1 hash of the content. For display, the hash is @ -- mapped back into the original text using this table. @ -- @ -- This table contains sensitive information and should not be shared @ -- with unauthorized users. @ -- @ CREATE TABLE IF NOT EXISTS concealed( @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content @ mtime INTEGER, -- Time created. Seconds since 1970 |
︙ | ︙ | |||
79 80 81 82 83 84 85 | @ cols TEXT, -- A color-key specification @ sqlcode TEXT -- An SQL SELECT statement for this report @ ); ; static void rebuild_update_schema(void){ int rc; | | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | @ cols TEXT, -- A color-key specification @ sqlcode TEXT -- An SQL SELECT statement for this report @ ); ; static void rebuild_update_schema(void){ int rc; db_multi_exec("%s", zSchemaUpdates1 /*safe-for-%s*/); db_multi_exec("%s", zSchemaUpdates2 /*safe-for-%s*/); rc = db_exists("SELECT 1 FROM sqlite_master" " WHERE name='user' AND sql GLOB '* mtime *'"); if( rc==0 ){ db_multi_exec( "CREATE TEMP TABLE temp_user AS SELECT * FROM user;" "DROP TABLE user;" |
︙ | ︙ | |||
133 134 135 136 137 138 139 | rc = db_exists("SELECT 1 FROM sqlite_master" " WHERE name='reportfmt' AND sql GLOB '* mtime *'"); if( rc==0 ){ db_multi_exec( "CREATE TEMP TABLE old_fmt AS SELECT * FROM reportfmt;" "DROP TABLE reportfmt;" ); | | | | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | rc = db_exists("SELECT 1 FROM sqlite_master" " WHERE name='reportfmt' AND sql GLOB '* mtime *'"); if( rc==0 ){ db_multi_exec( "CREATE TEMP TABLE old_fmt AS SELECT * FROM reportfmt;" "DROP TABLE reportfmt;" ); db_multi_exec("%s", zSchemaUpdates2/*safe-for-%s*/); db_multi_exec( "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)" " SELECT rn, owner, title, cols, sqlcode, now() FROM old_fmt;" "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)" " SELECT rn, owner, title || ' (' || rn || ')', cols, sqlcode, now()" " FROM old_fmt;" ); } rc = db_exists("SELECT 1 FROM sqlite_master" " WHERE name='concealed' AND sql GLOB '* mtime *'"); if( rc==0 ){ db_multi_exec( "ALTER TABLE concealed ADD COLUMN mtime INTEGER;" "UPDATE concealed SET mtime=now();" ); } } /* ** Variables used to store state information about an on-going "rebuild" ** or "deconstruct". */ static int totalSize; /* Total number of artifacts to process */ static int processCnt; /* Number processed so far */ |
︙ | ︙ | |||
225 226 227 228 229 230 231 | /* Fix up the "blob.size" field if needed. */ if( size!=blob_size(pBase) ){ db_multi_exec( "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid ); } | | | | | > | | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | /* Fix up the "blob.size" field if needed. */ if( size!=blob_size(pBase) ){ db_multi_exec( "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid ); } /* Find all children of artifact rid */ db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid"); db_bind_int(&q1, ":rid", rid); bag_init(&children); while( db_step(&q1)==SQLITE_ROW ){ int cid = db_column_int(&q1, 0); if( !bag_find(&bagDone, cid) ){ bag_insert(&children, cid); } } nChild = bag_count(&children); db_reset(&q1); /* Crosslink the artifact */ if( nChild==0 ){ pUse = pBase; }else{ blob_copy(©, pBase); pUse = © } if( zFNameFormat==0 ){ /* We are doing "fossil rebuild" */ manifest_crosslink(rid, pUse, MC_NONE); }else{ /* We are doing "fossil deconstruct" */ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); char *zFile = mprintf(zFNameFormat /*works-like:"%s:%s"*/, zUuid, zUuid+prefixLength); blob_write_to_file(pUse,zFile); free(zFile); free(zUuid); blob_reset(pUse); } assert( blob_is_reset(pUse) ); rebuild_step_done(rid); /* Call all children recursively */ rid = 0; for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){ static Stmt q2; int sz; db_static_prepare(&q2, "SELECT content, size FROM blob WHERE rid=:rid"); db_bind_int(&q2, ":rid", cid); |
︙ | ︙ | |||
343 344 345 346 347 348 349 | percent_complete(0); } rebuild_update_schema(); for(;;){ zTable = db_text(0, "SELECT name FROM sqlite_master /*scan*/" " WHERE type='table'" | | | > > | > > > | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 | percent_complete(0); } rebuild_update_schema(); for(;;){ zTable = db_text(0, "SELECT name FROM sqlite_master /*scan*/" " WHERE type='table'" " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user'," "'config','shun','private','reportfmt'," "'concealed','accesslog','modreq'," "'purgeevent','purgeitem')" " AND name NOT GLOB 'sqlite_*'" " AND name NOT GLOB 'fx_*'" ); if( zTable==0 ) break; db_multi_exec("DROP TABLE %Q", zTable); free(zTable); } db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/); ticket_create_table(0); shun_artifacts(); db_multi_exec( "INSERT INTO unclustered" " SELECT rid FROM blob EXCEPT SELECT rid FROM private" ); db_multi_exec( "DELETE FROM unclustered" " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))" ); db_multi_exec( "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" ); db_multi_exec( "UPDATE user SET mtime=strftime('%%s','now') WHERE mtime IS NULL" ); /* The following should be count(*) instead of max(rid). max(rid) is ** an adequate approximation, however, and is much faster for large ** repositories. */ totalSize = db_int(0, "SELECT max(rid) FROM blob"); incrSize = totalSize/100; totalSize += incrSize*2; |
︙ | ︙ | |||
409 410 411 412 413 414 415 | } }else{ db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid); rebuild_step_done(rid); } } db_finalize(&s); | | | 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | } }else{ db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid); rebuild_step_done(rid); } } db_finalize(&s); manifest_crosslink_end(MC_NONE); rebuild_tag_trunk(); if( ttyOutput && !g.fQuiet && totalSize>0 ){ processCnt += incrSize; percent_complete((processCnt*1000)/totalSize); } if( doClustering ) create_cluster(); if( ttyOutput && !g.fQuiet && totalSize>0 ){ |
︙ | ︙ | |||
431 432 433 434 435 436 437 | return errCnt; } /* ** Attempt to convert more full-text blobs into delta-blobs for ** storage efficiency. */ | | | 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | return errCnt; } /* ** Attempt to convert more full-text blobs into delta-blobs for ** storage efficiency. */ void extra_deltification(void){ Stmt q; int topid, previd, rid; int prevfnid, fnid; db_begin_transaction(); db_prepare(&q, "SELECT rid FROM event, blob" " WHERE blob.rid=event.objid" |
︙ | ︙ | |||
514 515 516 517 518 519 520 521 522 523 524 525 526 527 | ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS? ** ** Reconstruct the named repository database from the core ** records. Run this command after updating the fossil ** executable in a way that changes the database schema. ** ** Options: ** --cluster Compute clusters for unclustered artifacts ** --compress Strive to make the database as small as possible ** --force Force the rebuild to complete even if errors are seen ** --noverify Skip the verification of changes to the BLOB table ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) ** --randomize Scan artifacts in a random order ** --vacuum Run VACUUM on the database after rebuilding | > > > > > > < < < > > > > > > > > > > > > > > > > > > | | > | | 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 | ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS? ** ** Reconstruct the named repository database from the core ** records. Run this command after updating the fossil ** executable in a way that changes the database schema. ** ** Options: ** --analyze Run ANALYZE on the database after rebuilding ** --cluster Compute clusters for unclustered artifacts ** --compress Strive to make the database as small as possible ** --deanalyze Remove ANALYZE tables from the database ** --force Force the rebuild to complete even if errors are seen ** --ifneeded Only do the rebuild if it would change the schema version ** --index Always add in the full-text search index ** --noverify Skip the verification of changes to the BLOB table ** --noindex Always omit the full-text search index ** --pagesize N Set the database pagesize to N. (512..65536 and power of 2) ** --randomize Scan artifacts in a random order ** --stats Show artifact statistics after rebuilding ** --vacuum Run VACUUM on the database after rebuilding ** --wal Set Write-Ahead-Log journalling mode on the database ** ** See also: deconstruct, reconstruct */ void rebuild_database(void){ int forceFlag; int randomizeFlag; int errCnt; int omitVerify; int doClustering; const char *zPagesize; int newPagesize = 0; int activateWal; int runVacuum; int runDeanalyze; int runAnalyze; int runCompress; int showStats; int runReindex; int optNoIndex; int optIndex; int optIfNeeded; omitVerify = find_option("noverify",0,0)!=0; forceFlag = find_option("force","f",0)!=0; randomizeFlag = find_option("randomize", 0, 0)!=0; doClustering = find_option("cluster", 0, 0)!=0; runVacuum = find_option("vacuum",0,0)!=0; runDeanalyze = find_option("deanalyze",0,0)!=0; runAnalyze = find_option("analyze",0,0)!=0; runCompress = find_option("compress",0,0)!=0; zPagesize = find_option("pagesize",0,1); showStats = find_option("stats",0,0)!=0; optIndex = find_option("index",0,0)!=0; optNoIndex = find_option("noindex",0,0)!=0; optIfNeeded = find_option("ifneeded",0,0)!=0; if( zPagesize ){ newPagesize = atoi(zPagesize); if( newPagesize<512 || newPagesize>65536 || (newPagesize&(newPagesize-1))!=0 ){ fossil_fatal("page size must be a power of two between 512 and 65536"); } } activateWal = find_option("wal",0,0)!=0; if( g.argc==3 ){ db_open_repository(g.argv[2]); }else{ db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); if( g.argc!=2 ){ usage("?REPOSITORY-FILENAME?"); } db_close(1); db_open_repository(g.zRepositoryName); } runReindex = search_index_exists(); if( optIndex ) runReindex = 1; if( optNoIndex ) runReindex = 0; if( optIfNeeded && fossil_strcmp(db_get("aux-schema",""),AUX_SCHEMA_MAX)==0 ){ return; } /* We should be done with options.. */ verify_all_options(); db_begin_transaction(); search_drop_index(); ttyOutput = 1; errCnt = rebuild_db(randomizeFlag, 1, doClustering); reconstruct_private_table(); db_multi_exec( "REPLACE INTO config(name,value,mtime) VALUES('content-schema',%Q,now());" "REPLACE INTO config(name,value,mtime) VALUES('aux-schema',%Q,now());" "REPLACE INTO config(name,value,mtime) VALUES('rebuilt',%Q,now());", CONTENT_SCHEMA, AUX_SCHEMA_MAX, get_version() ); if( errCnt && !forceFlag ){ fossil_print( "%d errors. Rolling back changes. Use --force to force a commit.\n", errCnt ); db_end_transaction(1); |
︙ | ︙ | |||
604 605 606 607 608 609 610 | db_open_repository(g.zRepositoryName); if( newPagesize ){ db_multi_exec("PRAGMA page_size=%d", newPagesize); runVacuum = 1; } if( runDeanalyze ){ db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" | | > > | | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | db_open_repository(g.zRepositoryName); if( newPagesize ){ db_multi_exec("PRAGMA page_size=%d", newPagesize); runVacuum = 1; } if( runDeanalyze ){ db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" "DROP TABLE IF EXISTS sqlite_stat3;" "DROP TABLE IF EXISTS sqlite_stat4;"); } if( runAnalyze ){ fossil_print("Analyzing the database... "); fflush(stdout); db_multi_exec("ANALYZE;"); fossil_print("done\n"); } if( runVacuum ){ fossil_print("Vacuuming the database... "); fflush(stdout); db_multi_exec("VACUUM"); fossil_print("done\n"); } if( activateWal ){ db_multi_exec("PRAGMA journal_mode=WAL;"); } } if( runReindex ) search_rebuild_index(); if( showStats ){ static const struct { int idx; const char *zLabel; } aStat[] = { { CFTYPE_ANY, "Artifacts:" }, { CFTYPE_MANIFEST, "Manifests:" }, { CFTYPE_CLUSTER, "Clusters:" }, { CFTYPE_CONTROL, "Tags:" }, { CFTYPE_WIKI, "Wikis:" }, { CFTYPE_TICKET, "Tickets:" }, { CFTYPE_ATTACHMENT,"Attachments:" }, |
︙ | ︙ | |||
682 683 684 685 686 687 688 | usage("?REPOSITORY-FILENAME?"); } db_close(1); db_open_repository(g.zRepositoryName); } db_begin_transaction(); create_cluster(); | | | | 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 | usage("?REPOSITORY-FILENAME?"); } db_close(1); db_open_repository(g.zRepositoryName); } db_begin_transaction(); create_cluster(); db_end_transaction(0); } /* ** COMMAND: test-clusters ** ** Verify that all non-private and non-shunned artifacts are accessible ** through the cluster chain. */ void test_clusters_cmd(void){ Bag pending; Stmt q; int n; db_find_and_open_repository(0, 2); bag_init(&pending); db_multi_exec( "CREATE TEMP TABLE xdone(x INTEGER PRIMARY KEY);" "INSERT INTO xdone SELECT rid FROM unclustered;" "INSERT OR IGNORE INTO xdone SELECT rid FROM private;" "INSERT OR IGNORE INTO xdone" |
︙ | ︙ | |||
717 718 719 720 721 722 723 | bag_insert(&pending, db_column_int(&q, 0)); } db_finalize(&q); while( bag_count(&pending)>0 ){ Manifest *p; int rid = bag_first(&pending); int i; | | | 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 | bag_insert(&pending, db_column_int(&q, 0)); } db_finalize(&q); while( bag_count(&pending)>0 ){ Manifest *p; int rid = bag_first(&pending); int i; bag_remove(&pending, rid); p = manifest_get(rid, CFTYPE_CLUSTER, 0); if( p==0 ){ fossil_fatal("bad cluster: rid=%d", rid); } for(i=0; i<p->nCChild; i++){ const char *zUuid = p->azCChild[i]; |
︙ | ︙ | |||
785 786 787 788 789 790 791 792 793 794 | int bVerily = find_option("verily",0,0)!=0; int bForce = find_option("force", "f", 0)!=0; int privateOnly = find_option("private",0,0)!=0; int bNeedRebuild = 0; db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); db_close(1); db_open_repository(g.zRepositoryName); if( !bForce ){ Blob ans; char cReply; | > > > > < | 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 | int bVerily = find_option("verily",0,0)!=0; int bForce = find_option("force", "f", 0)!=0; int privateOnly = find_option("private",0,0)!=0; int bNeedRebuild = 0; db_find_and_open_repository(OPEN_ANY_SCHEMA, 2); db_close(1); db_open_repository(g.zRepositoryName); /* We should be done with options.. */ verify_all_options(); if( !bForce ){ Blob ans; char cReply; prompt_user( "Scrubbing the repository will permanently delete information.\n" "Changes cannot be undone. Continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply!='y' && cReply!='Y' ){ fossil_exit(1); } |
︙ | ︙ | |||
813 814 815 816 817 818 819 | "DELETE FROM config WHERE name GLOB 'peer-*';" "DELETE FROM config WHERE name GLOB 'login-group-*';" "DELETE FROM config WHERE name GLOB 'skin:*';" "DELETE FROM config WHERE name GLOB 'subrepo:*';" ); if( bVerily ){ db_multi_exec( | | | | | > > > > | 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 | "DELETE FROM config WHERE name GLOB 'peer-*';" "DELETE FROM config WHERE name GLOB 'login-group-*';" "DELETE FROM config WHERE name GLOB 'skin:*';" "DELETE FROM config WHERE name GLOB 'subrepo:*';" ); if( bVerily ){ db_multi_exec( "DELETE FROM concealed;\n" "UPDATE rcvfrom SET ipaddr='unknown';\n" "DROP TABLE IF EXISTS accesslog;\n" "UPDATE user SET photo=NULL, info='';\n" "DROP TABLE IF EXISTS purgeevent;\n" "DROP TABLE IF EXISTS purgeitem;\n" "DROP TABLE IF EXISTS admin_log;\n" "DROP TABLE IF EXISTS vcache;\n" ); } } if( !bNeedRebuild ){ db_end_transaction(0); db_multi_exec("VACUUM;"); }else{ |
︙ | ︙ | |||
854 855 856 857 858 859 860 | if( pEntry->d_name[0]=='.' ){ continue; } zUtf8Name = fossil_filename_to_utf8(pEntry->d_name); zSubpath = mprintf("%s/%s", zPath, zUtf8Name); fossil_filename_free(zUtf8Name); | > > > > | > > | | | | | | | | | | < | | > > | 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 | if( pEntry->d_name[0]=='.' ){ continue; } zUtf8Name = fossil_filename_to_utf8(pEntry->d_name); zSubpath = mprintf("%s/%s", zPath, zUtf8Name); fossil_filename_free(zUtf8Name); #ifdef _DIRENT_HAVE_D_TYPE if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) ? (file_isdir(zSubpath)==1) : (pEntry->d_type==DT_DIR) ) #else if( file_isdir(zSubpath)==1 ) #endif { recon_read_dir(zSubpath); }else{ blob_init(&path, 0, 0); blob_appendf(&path, "%s", zSubpath); if( blob_read_from_file(&aContent, blob_str(&path))==-1 ){ fossil_fatal("some unknown error occurred while reading \"%s\"", blob_str(&path)); } content_put(&aContent); blob_reset(&path); blob_reset(&aContent); fossil_print("\r%d", ++nFileRead); fflush(stdout); } free(zSubpath); } closedir(d); }else { fossil_fatal("encountered error %d while trying to open \"%s\".", errno, g.argv[3]); } fossil_filename_free(zUnicodePath); |
︙ | ︙ | |||
901 902 903 904 905 906 907 908 909 | } if( file_isdir(g.argv[3])!=1 ){ fossil_print("\"%s\" is not a directory\n\n", g.argv[3]); usage("FILENAME DIRECTORY"); } db_create_repository(g.argv[2]); db_open_repository(g.argv[2]); db_open_config(0); db_begin_transaction(); | > > > > | | | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 | } if( file_isdir(g.argv[3])!=1 ){ fossil_print("\"%s\" is not a directory\n\n", g.argv[3]); usage("FILENAME DIRECTORY"); } db_create_repository(g.argv[2]); db_open_repository(g.argv[2]); /* We should be done with options.. */ verify_all_options(); db_open_config(0); db_begin_transaction(); db_initial_setup(0, 0, 0); fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]); recon_read_dir(g.argv[3]); fossil_print("\nBuilding the Fossil repository...\n"); rebuild_db(0, 1, 1); reconstruct_private_table(); /* Skip the verify_before_commit() step on a reconstruct. Most artifacts ** will have been changed and verification therefore takes a really, really ** long time. */ verify_cancel(); db_end_transaction(0); fossil_print("project-id: %s\n", db_get("project-code", 0)); fossil_print("server-id: %s\n", db_get("server-code", 0)); zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); } /* ** COMMAND: deconstruct* ** ** Usage %fossil deconstruct ?OPTIONS? DESTINATION ** ** ** This command exports all artifacts of a given repository and ** writes all artifacts to the file system. The DESTINATION directory ** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where ** AABBBBBBBBB.. is the 40 character artifact ID, AA the first 2 characters. ** If -L|--prefixlength is given, the length (default 2) of the directory ** prefix can be set to 0,1,..,9 characters. ** ** Options: ** -R|--repository REPOSITORY deconstruct given REPOSITORY ** -L|--prefixlength N set the length of the names of the DESTINATION ** subdirectories to N ** --private Include private artifacts. ** ** See also: rebuild, reconstruct |
︙ | ︙ |
Changes to src/regexp.c.
︙ | ︙ | |||
125 126 127 128 129 130 131 | unsigned i; for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return; pSet->aState[pSet->nState++] = newState; } /* Extract the next unicode character from *pzIn and return it. Advance ** *pzIn to the first byte past the end of the character returned. To | | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | unsigned i; for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return; pSet->aState[pSet->nState++] = newState; } /* Extract the next unicode character from *pzIn and return it. Advance ** *pzIn to the first byte past the end of the character returned. To ** be clear: this routine converts utf8 to unicode. This routine is ** optimized for the common case where the next character is a single byte. */ static unsigned re_next_char(ReInput *p){ unsigned c; if( p->i>=p->mx ) return 0; c = p->z[p->i++]; if( c>=0x80 ){ |
︙ | ︙ | |||
189 190 191 192 193 194 195 | int c = RE_EOF+1; int cPrev = 0; int rc = 0; ReInput in; in.z = zIn; in.i = 0; | | | | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | int c = RE_EOF+1; int cPrev = 0; int rc = 0; ReInput in; in.z = zIn; in.i = 0; in.mx = nIn>=0 ? nIn : strlen((const char*)zIn); /* Look for the initial prefix match, if there is one. */ if( pRe->nInit ){ unsigned char x = pRe->zInit[0]; while( in.i+pRe->nInit<=in.mx && (zIn[in.i]!=x || strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0) ){ in.i++; } if( in.i+pRe->nInit>in.mx ) return 0; } |
︙ | ︙ | |||
301 302 303 304 305 306 307 | }else{ j++; } } } if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; if( hit ) re_add_state(pNext, x+n); | | | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | }else{ j++; } } } if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; if( hit ) re_add_state(pNext, x+n); break; } } } } for(i=0; i<pNext->nState; i++){ if( pRe->aOp[pNext->aState[i]]==RE_OP_ACCEPT ){ rc = 1; break; } } |
︙ | ︙ | |||
462 463 464 465 466 467 468 | int iStart; unsigned c; const char *zErr; while( (c = p->xNextChar(&p->sIn))!=0 ){ iStart = p->nState; switch( c ){ case '|': | | | | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 | int iStart; unsigned c; const char *zErr; while( (c = p->xNextChar(&p->sIn))!=0 ){ iStart = p->nState; switch( c ){ case '|': case '$': case ')': { p->sIn.i--; return 0; } case '(': { zErr = re_subcompile_re(p); if( zErr ) return zErr; if( rePeek(p)!=')' ) return "unmatched '('"; p->sIn.i++; break; } case '.': { if( rePeek(p)=='*' ){ re_append(p, RE_OP_ANYSTAR, 0); p->sIn.i++; }else{ re_append(p, RE_OP_ANY, 0); } break; } case '*': { if( iPrev<0 ) return "'*' without operand"; re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1); |
︙ | ︙ | |||
650 651 652 653 654 655 656 | re_free(pRe); return "unrecognized character"; } /* The following is a performance optimization. If the regex begins with ** ".*" (if the input regex lacks an initial "^") and afterwards there are ** one or more matching characters, enter those matching characters into | | | 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 | re_free(pRe); return "unrecognized character"; } /* The following is a performance optimization. If the regex begins with ** ".*" (if the input regex lacks an initial "^") and afterwards there are ** one or more matching characters, enter those matching characters into ** zInit[]. The re_match() routine can then search ahead in the input ** string looking for the initial match without having to run the whole ** regex engine over the string. Do not worry able trying to match ** unicode characters beyond plane 0 - those are very rare and this is ** just an optimization. */ if( pRe->aOp[0]==RE_OP_ANYSTAR ){ for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ unsigned x = pRe->aArg[i]; |
︙ | ︙ | |||
687 688 689 690 691 692 693 | ** pattern and the second argument is the string. So, the SQL statements: ** ** A REGEXP B ** ** is implemented as regexp(B,A). */ static void re_sql_func( | | | | 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 | ** pattern and the second argument is the string. So, the SQL statements: ** ** A REGEXP B ** ** is implemented as regexp(B,A). */ static void re_sql_func( sqlite3_context *context, int argc, sqlite3_value **argv ){ ReCompiled *pRe; /* Compiled regular expression */ const char *zPattern; /* The regular expression */ const unsigned char *zStr;/* String being searched */ const char *zErr; /* Compile error message */ |
︙ | ︙ | |||
766 767 768 769 770 771 772 | ** ** -i|--ignore-case Ignore case */ void re_test_grep(void){ ReCompiled *pRe; const char *zErr; int ignoreCase = find_option("ignore-case","i",0)!=0; | < | 766 767 768 769 770 771 772 773 774 775 776 777 778 779 | ** ** -i|--ignore-case Ignore case */ void re_test_grep(void){ ReCompiled *pRe; const char *zErr; int ignoreCase = find_option("ignore-case","i",0)!=0; if( g.argc<3 ){ usage("REGEXP [FILE...]"); } zErr = re_compile(&pRe, g.argv[2], ignoreCase); if( zErr ) fossil_fatal("%s", zErr); if( g.argc==3 ){ grep(pRe, "-", stdin); |
︙ | ︙ |
Changes to src/report.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* | | > > > > > > | > > > > | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Code to generate the ticket listings */ #include "config.h" #include <time.h> #include "report.h" #include <assert.h> /* Forward references to static routines */ static void report_format_hints(void); #ifndef SQLITE_RECURSIVE # define SQLITE_RECURSIVE 33 #endif /* ** WEBPAGE: /reportlist ** ** Main menu for Tickets. */ void view_list(void){ const char *zScript; Blob ril; /* Report Item List */ Stmt q; int rn = 0; int cnt = 0; login_check_credentials(); if( !g.perm.RdTkt && !g.perm.NewTkt ){ login_needed(g.anon.RdTkt || g.anon.NewTkt); return; } style_header("Ticket Main Menu"); ticket_standard_submenu(T_ALL_BUT(T_REPLIST)); if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br />\n", -1); zScript = ticket_reportlist_code(); if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br />\n", -1); blob_zero(&ril); ticket_init(); db_prepare(&q, "SELECT rn, title, owner FROM reportfmt ORDER BY title"); while( db_step(&q)==SQLITE_ROW ){ const char *zTitle = db_column_text(&q, 1); const char *zOwner = db_column_text(&q, 2); |
︙ | ︙ | |||
64 65 66 67 68 69 70 | if( g.perm.Write && zOwner && zOwner[0] ){ blob_appendf(&ril, "(by <i>%h</i>) ", zOwner); } if( g.perm.TktFmt ){ blob_appendf(&ril, "[%zcopy</a>] ", href("%R/rptedit?rn=%d©=1", rn)); } | | | | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | if( g.perm.Write && zOwner && zOwner[0] ){ blob_appendf(&ril, "(by <i>%h</i>) ", zOwner); } if( g.perm.TktFmt ){ blob_appendf(&ril, "[%zcopy</a>] ", href("%R/rptedit?rn=%d©=1", rn)); } if( g.perm.Admin || (g.perm.WrTkt && zOwner && fossil_strcmp(g.zLogin,zOwner)==0) ){ blob_appendf(&ril, "[%zedit</a>]", href("%R/rptedit?rn=%d", rn)); } if( g.perm.TktFmt ){ blob_appendf(&ril, "[%zsql</a>]", href("%R/rptsql?rn=%d", rn)); } blob_appendf(&ril, "</li>\n"); } db_finalize(&q); Th_Store("report_items", blob_str(&ril)); Th_Render(zScript); blob_reset(&ril); if( g.thTrace ) Th_Trace("END_REPORTLIST<br />\n", -1); style_footer(); } /* |
︙ | ︙ | |||
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | int rc = SQLITE_OK; if( *(char**)pError ){ /* We've already seen an error. No need to continue. */ return SQLITE_OK; } switch( code ){ case SQLITE_SELECT: case SQLITE_FUNCTION: { break; } case SQLITE_READ: { static const char *const azAllowed[] = { "ticket", "ticketchng", "blob", "filename", "mlink", "plink", "event", "tag", "tagxref", }; int i; for(i=0; i<sizeof(azAllowed)/sizeof(azAllowed[0]); i++){ if( fossil_stricmp(zArg1, azAllowed[i])==0 ) break; } if( i>=sizeof(azAllowed)/sizeof(azAllowed[0]) ){ *(char**)pError = mprintf("access to table \"%s\" is restricted",zArg1); rc = SQLITE_DENY; }else if( !g.perm.RdAddr && strncmp(zArg2, "private_", 8)==0 ){ | > > > > | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | int rc = SQLITE_OK; if( *(char**)pError ){ /* We've already seen an error. No need to continue. */ return SQLITE_OK; } switch( code ){ case SQLITE_SELECT: case SQLITE_RECURSIVE: case SQLITE_FUNCTION: { break; } case SQLITE_READ: { static const char *const azAllowed[] = { "ticket", "ticketchng", "blob", "filename", "mlink", "plink", "event", "tag", "tagxref", }; int i; if( fossil_strncmp(zArg1, "fx_", 3)==0 ){ break; } for(i=0; i<sizeof(azAllowed)/sizeof(azAllowed[0]); i++){ if( fossil_stricmp(zArg1, azAllowed[i])==0 ) break; } if( i>=sizeof(azAllowed)/sizeof(azAllowed[0]) ){ *(char**)pError = mprintf("access to table \"%s\" is restricted",zArg1); rc = SQLITE_DENY; }else if( !g.perm.RdAddr && strncmp(zArg2, "private_", 8)==0 ){ |
︙ | ︙ | |||
205 206 207 208 209 210 211 | return rc; } /* ** Activate the query authorizer */ static void report_restrict_sql(char **pzErr){ | < | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | return rc; } /* ** Activate the query authorizer */ static void report_restrict_sql(char **pzErr){ sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)pzErr); } static void report_unrestrict_sql(void){ sqlite3_set_authorizer(g.db, 0, 0); } |
︙ | ︙ | |||
227 228 229 230 231 232 233 | int i; char *zErr = 0; const char *zTail; sqlite3_stmt *pStmt; int rc; /* First make sure the SQL is a single query command by verifying that | | > | > | | | | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | int i; char *zErr = 0; const char *zTail; sqlite3_stmt *pStmt; int rc; /* First make sure the SQL is a single query command by verifying that ** the first token is "SELECT" or "WITH" and that there are no unquoted ** semicolons. */ for(i=0; fossil_isspace(zSql[i]); i++){} if( fossil_strnicmp(&zSql[i], "select", 6)!=0 && fossil_strnicmp(&zSql[i], "with", 4)!=0 ){ return mprintf("The SQL must be a SELECT or WITH statement"); } for(i=0; zSql[i]; i++){ if( zSql[i]==';' ){ int bad; int c = zSql[i+1]; zSql[i+1] = 0; bad = sqlite3_complete(zSql); zSql[i+1] = c; if( bad ){ /* A complete statement basically means that an unquoted semi-colon ** was found. We don't actually check what's after that. */ return mprintf("Semi-colon detected! " "Only a single SQL statement is allowed"); } } } /* Compile the statement and check for illegal accesses or syntax errors. */ report_restrict_sql(&zErr); rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt, &zTail); if( rc!=SQLITE_OK ){ zErr = mprintf("Syntax error: %s", sqlite3_errmsg(g.db)); } if( !sqlite3_stmt_readonly(pStmt) ){ zErr = mprintf("SQL must not modify the database"); } if( pStmt ){ |
︙ | ︙ | |||
279 280 281 282 283 284 285 | const char *zSQL; const char *zOwner; const char *zClrKey; Stmt q; login_check_credentials(); if( !g.perm.TktFmt ){ | | > | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | const char *zSQL; const char *zOwner; const char *zClrKey; Stmt q; login_check_credentials(); if( !g.perm.TktFmt ){ login_needed(g.anon.TktFmt); return; } rn = atoi(PD("rn","0")); db_prepare(&q, "SELECT title, sqlcode, owner, cols " "FROM reportfmt WHERE rn=%d",rn); style_header("SQL For Report Format Number %d", rn); if( db_step(&q)!=SQLITE_ROW ){ @ <p>Unknown report number: %d(rn)</p> style_footer(); db_finalize(&q); return; } zTitle = db_column_text(&q, 0); zSQL = db_column_text(&q, 1); zOwner = db_column_text(&q, 2); zClrKey = db_column_text(&q, 3); @ <table cellpadding=0 cellspacing=0 border=0> |
︙ | ︙ | |||
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | @ </pre></td> @ <td width=15></td><td valign="top"> output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3"); @ </td> @ </tr></table> report_format_hints(); style_footer(); } /* ** WEBPAGE: /rptnew ** WEBPAGE: /rptedit */ void view_edit(void){ int rn; const char *zTitle; const char *z; const char *zOwner; const char *zClrKey; char *zSQL; char *zErr = 0; login_check_credentials(); if( !g.perm.TktFmt ){ | > | | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | @ </pre></td> @ <td width=15></td><td valign="top"> output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3"); @ </td> @ </tr></table> report_format_hints(); style_footer(); db_finalize(&q); } /* ** WEBPAGE: /rptnew ** WEBPAGE: /rptedit */ void view_edit(void){ int rn; const char *zTitle; const char *z; const char *zOwner; const char *zClrKey; char *zSQL; char *zErr = 0; login_check_credentials(); if( !g.perm.TktFmt ){ login_needed(g.anon.TktFmt); return; } /*view_add_functions(0);*/ rn = atoi(PD("rn","0")); zTitle = P("t"); zOwner = PD("w",g.zLogin); z = P("s"); |
︙ | ︙ | |||
370 371 372 373 374 375 376 | cgi_redirect("reportlist"); return; } if( zTitle && zSQL ){ if( zSQL[0]==0 ){ zErr = "Please supply an SQL query statement"; }else if( (zTitle = trim_string(zTitle))[0]==0 ){ | | | 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | cgi_redirect("reportlist"); return; } if( zTitle && zSQL ){ if( zSQL[0]==0 ){ zErr = "Please supply an SQL query statement"; }else if( (zTitle = trim_string(zTitle))[0]==0 ){ zErr = "Please supply a title"; }else{ zErr = verify_sql_statement(zSQL); } if( zErr==0 && db_exists("SELECT 1 FROM reportfmt WHERE title=%Q and rn<>%d", zTitle, rn) ){ |
︙ | ︙ | |||
421 422 423 424 425 426 427 | } } if( zOwner==0 ) zOwner = g.zLogin; style_submenu_element("Cancel", "Cancel", "reportlist"); if( rn>0 ){ style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn); } | | | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | } } if( zOwner==0 ) zOwner = g.zLogin; style_submenu_element("Cancel", "Cancel", "reportlist"); if( rn>0 ){ style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn); } style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format"); if( zErr ){ @ <blockquote class="reportError">%h(zErr)</blockquote> } @ <form action="rptedit" method="post"><div> @ <input type="hidden" name="rn" value="%d(rn)" /> @ <p>Report Title:<br /> @ <input type="text" name="t" value="%h(zTitle)" size="60" /></p> |
︙ | ︙ | |||
601 602 603 604 605 606 607 | @ priority AS 'Pri', @ title AS 'Title', @ description AS '_Description', -- When the column name begins with '_' @ remarks AS '_Remarks' -- content is rendered as wiki @ FROM ticket @ </pre></blockquote> @ | < < < < < < < < < < < < < | 618 619 620 621 622 623 624 625 626 627 628 629 630 631 | @ priority AS 'Pri', @ title AS 'Title', @ description AS '_Description', -- When the column name begins with '_' @ remarks AS '_Remarks' -- content is rendered as wiki @ FROM ticket @ </pre></blockquote> @ } /* ** The state of the report generation. */ struct GenerateHTML { int rn; /* Report number */ |
︙ | ︙ | |||
637 638 639 640 641 642 643 | /* ** The callback function for db_query */ static int generate_html( void *pUser, /* Pointer to output state */ int nArg, /* Number of columns in this result row */ | | | | | 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 | /* ** The callback function for db_query */ static int generate_html( void *pUser, /* Pointer to output state */ int nArg, /* Number of columns in this result row */ const char **azArg, /* Text of data in all columns */ const char **azName /* Names of the columns */ ){ struct GenerateHTML *pState = (struct GenerateHTML*)pUser; int i; const char *zTid; /* Ticket UUID. (value of column named '#') */ const char *zBg = 0; /* Use this background color */ /* Do initialization */ if( pState->nCount==0 ){ /* Turn off the authorizer. It is no longer doing anything since the ** query has already been prepared. */ |
︙ | ︙ | |||
697 698 699 700 701 702 703 | } /* The first time this routine is called, output a table header */ @ <thead><tr> zTid = 0; for(i=0; i<nArg; i++){ | | | 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 | } /* The first time this routine is called, output a table header */ @ <thead><tr> zTid = 0; for(i=0; i<nArg; i++){ const char *zName = azName[i]; if( i==pState->iBg ) continue; if( pState->iNewRow>=0 && i>=pState->iNewRow ){ if( g.perm.Write && zTid ){ @ <th> </th> zTid = 0; } if( zName[0]=='_' ) zName++; |
︙ | ︙ | |||
740 741 742 743 744 745 746 | /* Output the data for this entry from the database */ zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0; if( zBg==0 ) zBg = "white"; @ <tr style="background-color:%h(zBg)"> zTid = 0; for(i=0; i<nArg; i++){ | | | 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | /* Output the data for this entry from the database */ zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0; if( zBg==0 ) zBg = "white"; @ <tr style="background-color:%h(zBg)"> zTid = 0; for(i=0; i<nArg; i++){ const char *zData; if( i==pState->iBg ) continue; zData = azArg[i]; if( zData==0 ) zData = ""; if( pState->iNewRow>=0 && i>=pState->iNewRow ){ if( zTid && g.perm.Write ){ @ <td valign="top">%z(href("%R/tktedit/%h",zTid))edit</a></td> zTid = 0; |
︙ | ︙ | |||
802 803 804 805 806 807 808 | /* ** Output a row as a tab-separated line of text. */ static int output_tab_separated( void *pUser, /* Pointer to row-count integer */ int nArg, /* Number of columns in this result row */ | | | | 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | /* ** Output a row as a tab-separated line of text. */ static int output_tab_separated( void *pUser, /* Pointer to row-count integer */ int nArg, /* Number of columns in this result row */ const char **azArg, /* Text of data in all columns */ const char **azName /* Names of the columns */ ){ int *pCount = (int*)pUser; int i; if( *pCount==0 ){ for(i=0; i<nArg; i++){ output_no_tabs(azName[i]); |
︙ | ︙ | |||
827 828 829 830 831 832 833 | } /* ** Generate HTML that describes a color key. */ void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){ int i, j, k; | | > | | 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 | } /* ** Generate HTML that describes a color key. */ void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){ int i, j, k; const char *zSafeKey; char *zToFree; while( fossil_isspace(*zClrKey) ) zClrKey++; if( zClrKey[0]==0 ) return; @ <table %s(zTabArgs)> if( horiz ){ @ <tr> } zSafeKey = zToFree = mprintf("%h", zClrKey); while( zSafeKey[0] ){ while( fossil_isspace(*zSafeKey) ) zSafeKey++; for(i=0; zSafeKey[i] && !fossil_isspace(zSafeKey[i]); i++){} for(j=i; fossil_isspace(zSafeKey[j]); j++){} for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){} if( !horiz ){ cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n", |
︙ | ︙ | |||
860 861 862 863 864 865 866 | @ </table> } /* ** Execute a single read-only SQL statement. Invoke xCallback() on each ** row. */ | | > | | | | 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 | @ </table> } /* ** Execute a single read-only SQL statement. Invoke xCallback() on each ** row. */ static int db_exec_readonly( sqlite3 *db, /* The database on which the SQL executes */ const char *zSql, /* The SQL to be executed */ int (*xCallback)(void*,int,const char**, const char**), /* Invoke this callback routine */ void *pArg, /* First argument to xCallback() */ char **pzErrMsg /* Write error messages here */ ){ int rc = SQLITE_OK; /* Return code */ const char *zLeftover; /* Tail of unprocessed SQL */ sqlite3_stmt *pStmt = 0; /* The current SQL statement */ const char **azCols = 0; /* Names of result columns */ int nCol; /* Number of columns of output */ const char **azVals = 0; /* Text of all output columns */ int i; /* Loop counter */ pStmt = 0; rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); assert( rc==SQLITE_OK || pStmt==0 ); if( rc!=SQLITE_OK ){ return rc; |
︙ | ︙ | |||
899 900 901 902 903 904 905 | nCol = sqlite3_column_count(pStmt); azVals = fossil_malloc(2*nCol*sizeof(const char*) + 1); while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){ if( azCols==0 ){ azCols = &azVals[nCol]; for(i=0; i<nCol; i++){ | | | | | > > > > | > > > > | | > > > > > > > | > > > > | > | > > > > > > < | | | | > > > > | > > > > > > > > > > > > > > > > > > | > > | > > > > > > | > > | > > | > | | > | | 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 | nCol = sqlite3_column_count(pStmt); azVals = fossil_malloc(2*nCol*sizeof(const char*) + 1); while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){ if( azCols==0 ){ azCols = &azVals[nCol]; for(i=0; i<nCol; i++){ azCols[i] = sqlite3_column_name(pStmt, i); } } for(i=0; i<nCol; i++){ azVals[i] = (const char *)sqlite3_column_text(pStmt, i); } if( xCallback(pArg, nCol, azVals, azCols) ){ break; } } rc = sqlite3_finalize(pStmt); fossil_free((void *)azVals); return rc; } /* ** Output Javascript code that will enables sorting of the table with ** the id zTableId by clicking. ** ** The javascript was originally derived from: ** ** http://www.webtoolkit.info/sortable-html-table.html ** ** But there have been extensive modifications. ** ** This variation allows column types to be expressed using the second ** argument. Each character of the second argument represent a column. ** ** t Sort by text ** n Sort numerically ** k Sort by the data-sortkey property ** x This column is not sortable ** ** Capital letters mean sort in reverse order. ** If there are fewer characters in zColumnTypes[] than their are columns, ** the all extra columns assume type "t" (text). ** ** The third parameter is the column that was initially sorted (using 1-based ** column numbers, like SQL). Make this value 0 if none of the columns are ** initially sorted. Make the value negative if the column is initially sorted ** in reverse order. ** ** Clicking on the same column header twice in a row inverts the sort. */ void output_table_sorting_javascript( const char *zTableId, /* ID of table to sort */ const char *zColumnTypes, /* String for column types */ int iInitSort /* Initially sorted column. Leftmost is 1. 0 for NONE */ ){ @ <script> @ function SortableTable(tableEl,columnTypes,initSort){ @ this.tbody = tableEl.getElementsByTagName('tbody'); @ this.columnTypes = columnTypes; @ this.sort = function (cell) { @ var column = cell.cellIndex; @ var sortFn; @ switch( cell.sortType ){ @ case "N": case "n": sortFn = this.sortNumeric; break; @ case "T": case "t": sortFn = this.sortText; break; @ case "K": case "k": sortFn = this.sortKey; break; @ default: return; @ } @ this.sortIndex = column; @ var newRows = new Array(); @ for (j = 0; j < this.tbody[0].rows.length; j++) { @ newRows[j] = this.tbody[0].rows[j]; @ } @ if( this.sortIndex==Math.abs(this.prevColumn)-1 ){ @ newRows.reverse(); @ this.prevColumn = -this.prevColumn; @ }else{ @ newRows.sort(sortFn); @ this.prevColumn = this.sortIndex+1; @ if( cell.sortType>="A" && cell.sortType<="Z" ){ @ newRows.reverse(); @ } @ } @ for (i=0;i<newRows.length;i++) { @ this.tbody[0].appendChild(newRows[i]); @ } @ this.setHdrIcons(); @ } @ this.setHdrIcons = function() { @ for (var i=0; i<this.hdrRow.cells.length; i++) { @ if( this.columnTypes[i]=='x' ) continue; @ var sortType; @ if( this.prevColumn==i+1 ){ @ sortType = 'asc'; @ }else if( this.prevColumn==(-1-i) ){ @ sortType = 'desc' @ }else{ @ sortType = 'none'; @ } @ var hdrCell = this.hdrRow.cells[i]; @ var clsName = hdrCell.className.replace(/\s*\bsort\s*\w+/, ''); @ clsName += ' sort ' + sortType; @ hdrCell.className = clsName; @ } @ } @ this.sortText = function(a,b) { @ var i = thisObject.sortIndex; @ aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); @ bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); @ if(aa==bb) return a.rowIndex-b.rowIndex; @ if(aa<bb) return -1; @ return 1; @ } @ this.sortNumeric = function(a,b) { @ var i = thisObject.sortIndex; @ aa = parseFloat(a.cells[i].textContent); @ if (isNaN(aa)) aa = 0; @ bb = parseFloat(b.cells[i].textContent); @ if (isNaN(bb)) bb = 0; @ if(aa==bb) return a.rowIndex-b.rowIndex; @ return aa-bb; @ } @ this.sortKey = function(a,b) { @ var i = thisObject.sortIndex; @ aa = a.cells[i].getAttribute("data-sortkey"); @ bb = b.cells[i].getAttribute("data-sortkey"); @ if(aa==bb) return a.rowIndex-b.rowIndex; @ if(aa<bb) return -1; @ return 1; @ } @ var x = tableEl.getElementsByTagName('thead'); @ if(!(this.tbody && this.tbody[0].rows && this.tbody[0].rows.length>0)){ @ return; @ } @ if(x && x[0].rows && x[0].rows.length > 0) { @ this.hdrRow = x[0].rows[0]; @ } else { @ return; @ } @ var thisObject = this; @ this.prevColumn = initSort; @ for (var i=0; i<this.hdrRow.cells.length; i++) { @ if( columnTypes[i]=='x' ) continue; @ var hdrcell = this.hdrRow.cells[i]; @ hdrcell.sTable = this; @ hdrcell.style.cursor = "pointer"; @ hdrcell.sortType = columnTypes[i] || 't'; @ hdrcell.onclick = function () { @ this.sTable.sort(this); @ return false; @ } @ } @ this.setHdrIcons() @ } @ var t = new SortableTable(gebi("%s(zTableId)"),"%s(zColumnTypes)",%d(iInitSort)); @ </script> } /* ** WEBPAGE: /rptview ** |
︙ | ︙ | |||
1012 1013 1014 1015 1016 1017 1018 | char *zClrKey; int tabs; Stmt q; char *zErr1 = 0; char *zErr2 = 0; login_check_credentials(); | | > | 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 | char *zClrKey; int tabs; Stmt q; char *zErr1 = 0; char *zErr2 = 0; login_check_credentials(); if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } rn = atoi(PD("rn","0")); if( rn==0 ){ cgi_redirect("reportlist"); return; } tabs = P("tablist")!=0; /* view_add_functions(tabs); */ db_prepare(&q, "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn); if( db_step(&q)!=SQLITE_ROW ){ cgi_redirect("reportlist"); db_finalize(&q); return; } zTitle = db_column_malloc(&q, 0); zSql = db_column_malloc(&q, 1); zOwner = db_column_malloc(&q, 2); zClrKey = db_column_malloc(&q, 3); db_finalize(&q); |
︙ | ︙ | |||
1052 1053 1054 1055 1056 1057 1058 | } count = 0; if( !tabs ){ struct GenerateHTML sState; db_multi_exec("PRAGMA empty_result_callbacks=ON"); | | | | | | | | | 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 | } count = 0; if( !tabs ){ struct GenerateHTML sState; db_multi_exec("PRAGMA empty_result_callbacks=ON"); style_submenu_element("Raw", "Raw", "rptview?tablist=1&%h", PD("QUERY_STRING","")); if( g.perm.Admin || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){ style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn); } if( g.perm.TktFmt ){ style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn); } if( g.perm.NewTkt ){ style_submenu_element("New Ticket", "Create a new ticket", "%s/tktnew", g.zTop); } style_header("%s", zTitle); output_color_key(zClrKey, 1, "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\""); @ <table border="1" cellpadding="2" cellspacing="0" class="report" @ id="reportTable"> sState.rn = rn; sState.nCount = 0; report_restrict_sql(&zErr1); db_exec_readonly(g.db, zSql, generate_html, &sState, &zErr2); report_unrestrict_sql(); @ </tbody></table> if( zErr1 ){ @ <p class="reportError">Error: %h(zErr1)</p> }else if( zErr2 ){ @ <p class="reportError">Error: %h(zErr2)</p> } output_table_sorting_javascript("reportTable","",0); style_footer(); }else{ report_restrict_sql(&zErr1); db_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2); report_unrestrict_sql(); cgi_set_content_type("text/plain"); } } /* ** report number for full table ticket export |
︙ | ︙ | |||
1107 1108 1109 1110 1111 1112 1113 | /* ** show all reports, which can be used for ticket show. ** Output is written to stdout as tab delimited table */ void rpt_list_reports(void){ Stmt q; | < < | | | | 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 | /* ** show all reports, which can be used for ticket show. ** Output is written to stdout as tab delimited table */ void rpt_list_reports(void){ Stmt q; fossil_print("Available reports:\n"); fossil_print("%s\t%s\n","report number","report title"); fossil_print("%s\t%s\n",zFullTicketRptRn,zFullTicketRptTitle); db_prepare(&q,"SELECT rn,title FROM reportfmt ORDER BY rn"); while( db_step(&q)==SQLITE_ROW ){ const char *zRn = db_column_text(&q, 0); const char *zTitle = db_column_text(&q, 1); fossil_print("%s\t%s\n",zRn,zTitle); } db_finalize(&q); } /* ** user defined separator used by ticket show command */ |
︙ | ︙ | |||
1164 1165 1166 1167 1168 1169 1170 | } for(j=i; fossil_isspace(z[j]); j++){} if( j>i ){ fossil_print("%*s", j-i, ""); } z += j; } | | | | | 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 | } for(j=i; fossil_isspace(z[j]); j++){} if( j>i ){ fossil_print("%*s", j-i, ""); } z += j; } break; } } /* ** Output a row as a tab-separated line of text. */ int output_separated_file( void *pUser, /* Pointer to row-count integer */ int nArg, /* Number of columns in this result row */ const char **azArg, /* Text of data in all columns */ const char **azName /* Names of the columns */ ){ int *pCount = (int*)pUser; int i; if( *pCount==0 ){ for(i=0; i<nArg; i++){ output_no_tabs_file(azName[i]); |
︙ | ︙ | |||
1199 1200 1201 1202 1203 1204 1205 | } /* ** Generate a report. The rn query parameter is the report number. ** The output is written to stdout as flat file. The zFilter parameter ** is a full WHERE-condition. */ | | | | 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 | } /* ** Generate a report. The rn query parameter is the report number. ** The output is written to stdout as flat file. The zFilter parameter ** is a full WHERE-condition. */ void rptshow( const char *zRep, const char *zSepIn, const char *zFilter, tTktShowEncoding enc ){ Stmt q; char *zSql; char *zErr1 = 0; char *zErr2 = 0; int count = 0; int rn; if( !zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){ zSql = "SELECT * FROM ticket"; }else{ rn = atoi(zRep); if( rn ){ db_prepare(&q, "SELECT sqlcode FROM reportfmt WHERE rn=%d", rn); }else{ |
︙ | ︙ | |||
1238 1239 1240 1241 1242 1243 1244 | if( zFilter ){ zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter); } count = 0; tktEncode = enc; zSep = zSepIn; report_restrict_sql(&zErr1); | | | 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 | if( zFilter ){ zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter); } count = 0; tktEncode = enc; zSep = zSepIn; report_restrict_sql(&zErr1); db_exec_readonly(g.db, zSql, output_separated_file, &count, &zErr2); report_unrestrict_sql(); if( zFilter ){ free(zSql); } } |
Changes to src/rss.c.
︙ | ︙ | |||
24 25 26 27 28 29 30 | /* ** WEBPAGE: timeline.rss ** URL: /timeline.rss?y=TYPE&n=LIMIT&tkt=UUID&tag=TAG&wiki=NAME&name=FILENAME ** ** Produce an RSS feed of the timeline. ** | | | > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | /* ** WEBPAGE: timeline.rss ** URL: /timeline.rss?y=TYPE&n=LIMIT&tkt=UUID&tag=TAG&wiki=NAME&name=FILENAME ** ** Produce an RSS feed of the timeline. ** ** TYPE may be: all, ci (show check-ins only), t (show tickets only), ** w (show wiki only). ** ** LIMIT is the number of items to show. ** ** tkt=UUID filters for only those events for the specified ticket. tag=TAG ** filters for a tag, and wiki=NAME for a wiki page. Only one may be used. ** ** In addition, name=FILENAME filters for a specific file. This may be ** combined with one of the other filters (useful for looking at a specific ** branch). |
︙ | ︙ | |||
61 62 63 64 65 66 67 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), @ (SELECT count(*) FROM plink WHERE cid=blob.rid) @ FROM event, blob @ WHERE blob.rid=event.objid ; login_check_credentials(); | | | | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), @ (SELECT count(*) FROM plink WHERE cid=blob.rid) @ FROM event, blob @ WHERE blob.rid=event.objid ; login_check_credentials(); if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){ return; } blob_zero(&bSQL); blob_append( &bSQL, zSQL1, -1 ); if( zType[0]!='a' ){ if( zType[0]=='c' && !g.perm.Read ) zType = "x"; if( zType[0]=='w' && !g.perm.RdWiki ) zType = "x"; if( zType[0]=='t' && !g.perm.RdTkt ) zType = "x"; blob_append_sql(&bSQL, " AND event.type=%Q", zType); }else{ if( !g.perm.Read ){ if( g.perm.RdTkt && g.perm.RdWiki ){ blob_append(&bSQL, " AND event.type!='ci'", -1); }else if( g.perm.RdTkt ){ blob_append(&bSQL, " AND event.type=='t'", -1); }else{ blob_append(&bSQL, " AND event.type=='w'", -1); } }else if( !g.perm.RdWiki ){ if( g.perm.RdTkt ){ blob_append(&bSQL, " AND event.type!='w'", -1); }else{ |
︙ | ︙ | |||
118 119 120 121 122 123 124 | nTagId = -1; } }else{ nTagId = 0; } if( nTagId==-1 ){ | | | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | nTagId = -1; } }else{ nTagId = 0; } if( nTagId==-1 ){ blob_append_sql(&bSQL, " AND 0"); }else if( nTagId!=0 ){ blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId); } if( zFilename ){ blob_append_sql(&bSQL, " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)", zFilename, filename_collation() ); } blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 ); |
︙ | ︙ | |||
156 157 158 159 160 161 162 | @ <channel> @ <title>%h(zProjectName)</title> @ <link>%s(g.zBaseURL)</link> @ <description>%h(zProjectDescr)</description> @ <pubDate>%s(zPubDate)</pubDate> @ <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator> free(zPubDate); | | | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | @ <channel> @ <title>%h(zProjectName)</title> @ <link>%s(g.zBaseURL)</link> @ <description>%h(zProjectDescr)</description> @ <pubDate>%s(zPubDate)</pubDate> @ <generator>Fossil version %s(MANIFEST_VERSION) %s(MANIFEST_DATE)</generator> free(zPubDate); db_prepare(&q, "%s", blob_sql_text(&bSQL)); blob_reset( &bSQL ); while( db_step(&q)==SQLITE_ROW && nLine<nLimit ){ const char *zId = db_column_text(&q, 1); const char *zCom = db_column_text(&q, 3); const char *zAuthor = db_column_text(&q, 4); char *zPrefix = ""; char *zDate; int nChild = db_column_int(&q, 5); int nParent = db_column_int(&q, 6); |
︙ | ︙ | |||
194 195 196 197 198 199 200 201 202 203 204 205 | free(zDate); nLine++; } db_finalize(&q); @ </channel> @ </rss> if( zFreeProjectName != 0 ){ free( zFreeProjectName ); } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | free(zDate); nLine++; } db_finalize(&q); @ </channel> @ </rss> if( zFreeProjectName != 0 ){ free( zFreeProjectName ); } } /* ** COMMAND: rss ** ** Usage: %fossil rss ?OPTIONS? ** ** The CLI variant of the /timeline.rss page, this produces an RSS ** feed of the timeline to stdout. Options: ** ** -type|y FLAG ** may be: all (default), ci (show check-ins only), t (show tickets only), ** w (show wiki only). ** ** -limit|n LIMIT ** The maximum number of items to show. ** ** -tkt UUID ** Filters for only those events for the specified ticket. ** ** -tag TAG ** filters for a tag ** ** -wiki NAME ** Filters on a specific wiki page. ** ** Only one of -tkt, -tag, or -wiki may be used. ** ** -name FILENAME ** filters for a specific file. This may be combined with one of the other ** filters (useful for looking at a specific branch). ** ** -url STRING ** Sets the RSS feed's root URL to the given string. The default is ** "URL-PLACEHOLDER" (without quotes). */ void cmd_timeline_rss(void){ Stmt q; int nLine=0; char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0; Blob bSQL; const char *zType = find_option("type","y",1); /* Type of events. All if NULL */ const char *zTicketUuid = find_option("tkt",NULL,1); const char *zTag = find_option("tag",NULL,1); const char *zFilename = find_option("name",NULL,1); const char *zWiki = find_option("wiki",NULL,1); const char *zLimit = find_option("limit", "n",1); const char *zBaseURL = find_option("url", NULL, 1); int nLimit = atoi( (zLimit && *zLimit) ? zLimit : "20" ); int nTagId; const char zSQL1[] = @ SELECT @ blob.rid, @ uuid, @ event.mtime, @ coalesce(ecomment,comment), @ coalesce(euser,user), @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), @ (SELECT count(*) FROM plink WHERE cid=blob.rid) @ FROM event, blob @ WHERE blob.rid=event.objid ; if(!zType || !*zType){ zType = "all"; } if(!zBaseURL || !*zBaseURL){ zBaseURL = "URL-PLACEHOLDER"; } db_find_and_open_repository(0, 0); /* We should be done with options.. */ verify_all_options(); blob_zero(&bSQL); blob_append( &bSQL, zSQL1, -1 ); if( zType[0]!='a' ){ blob_append_sql(&bSQL, " AND event.type=%Q", zType); } if( zTicketUuid ){ nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'", zTicketUuid); if ( nTagId==0 ){ nTagId = -1; } }else if( zTag ){ nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q*'", zTag); if ( nTagId==0 ){ nTagId = -1; } }else if( zWiki ){ nTagId = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'wiki-%q*'", zWiki); if ( nTagId==0 ){ nTagId = -1; } }else{ nTagId = 0; } if( nTagId==-1 ){ blob_append_sql(&bSQL, " AND 0"); }else if( nTagId!=0 ){ blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId); } if( zFilename ){ blob_append_sql(&bSQL, " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)", zFilename, filename_collation() ); } blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 ); zProjectName = db_get("project-name", 0); if( zProjectName==0 ){ zFreeProjectName = zProjectName = mprintf("Fossil source repository for: %s", zBaseURL); } zProjectDescr = db_get("project-description", 0); if( zProjectDescr==0 ){ zProjectDescr = zProjectName; } zPubDate = cgi_rfc822_datestamp(time(NULL)); fossil_print("<?xml version=\"1.0\"?>"); fossil_print("<rss xmlns:dc=\"http://purl.org/dc/elements/1.1/\" version=\"2.0\">"); fossil_print("<channel>\n"); fossil_print("<title>%h</title>\n", zProjectName); fossil_print("<link>%s</link>\n", zBaseURL); fossil_print("<description>%h</description>\n", zProjectDescr); fossil_print("<pubDate>%s</pubDate>\n", zPubDate); fossil_print("<generator>Fossil version %s %s</generator>\n", MANIFEST_VERSION, MANIFEST_DATE); free(zPubDate); db_prepare(&q, "%s", blob_sql_text(&bSQL)); blob_reset( &bSQL ); while( db_step(&q)==SQLITE_ROW && nLine<nLimit ){ const char *zId = db_column_text(&q, 1); const char *zCom = db_column_text(&q, 3); const char *zAuthor = db_column_text(&q, 4); char *zPrefix = ""; char *zDate; int nChild = db_column_int(&q, 5); int nParent = db_column_int(&q, 6); time_t ts; ts = (time_t)((db_column_double(&q,2) - 2440587.5)*86400.0); zDate = cgi_rfc822_datestamp(ts); if( nParent>1 && nChild>1 ){ zPrefix = "*MERGE/FORK* "; }else if( nParent>1 ){ zPrefix = "*MERGE* "; }else if( nChild>1 ){ zPrefix = "*FORK* "; } fossil_print("<item>"); fossil_print("<title>%s%h</title>\n", zPrefix, zCom); fossil_print("<link>%s/info/%s</link>\n", zBaseURL, zId); fossil_print("<description>%s%h</description>\n", zPrefix, zCom); fossil_print("<pubDate>%s</pubDate>\n", zDate); fossil_print("<dc:creator>%h</dc:creator>\n", zAuthor); fossil_print("<guid>%s/info/%s</guid>\n", g.zBaseURL, zId); fossil_print("</item>\n"); free(zDate); nLine++; } db_finalize(&q); fossil_print("</channel>\n"); fossil_print("</rss>\n"); if( zFreeProjectName != 0 ){ free( zFreeProjectName ); } } |
Changes to src/schema.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* | | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains string constants that implement the database schema. */ #include "config.h" #include "schema.h" /* ** The database schema for the ~/.fossil configuration database. */ const char zConfigSchema[] = @ -- This file contains the schema for the database that is kept in the @ -- ~/.fossil file and that stores information about the users setup. @ -- @ CREATE TABLE global_config( @ name TEXT PRIMARY KEY, @ value TEXT @ ); |
︙ | ︙ | |||
42 43 44 45 46 47 48 | ** The content tables have a content version number which rarely ** changes. The aux tables have an arbitrary version number (typically ** a date) which can change frequently. When the content schema changes, ** we have to execute special procedures to update the schema. When ** the aux schema changes, all we need to do is rebuild the database. */ #define CONTENT_SCHEMA "2" | > | > > > | | | | | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | ** The content tables have a content version number which rarely ** changes. The aux tables have an arbitrary version number (typically ** a date) which can change frequently. When the content schema changes, ** we have to execute special procedures to update the schema. When ** the aux schema changes, all we need to do is rebuild the database. */ #define CONTENT_SCHEMA "2" #define AUX_SCHEMA_MIN "2011-04-25 19:50" #define AUX_SCHEMA_MAX "2015-01-24" /* NB: Some features require the latest schema. Warning or error messages ** will appear if an older schema is used. However, the older schemas are ** adequate for many common functions. */ #endif /* INTERFACE */ /* ** The schema for a repository database. ** ** Schema1[] contains parts of the schema that are fixed and unchanging ** across versions. Schema2[] contains parts of the schema that can ** change from one version to the next. The information in Schema2[] ** is reconstructed from the information in Schema1[] by the "rebuild" ** operation. */ const char zRepositorySchema1[] = @ -- The BLOB and DELTA tables contain all records held in the repository. @ -- @ -- The BLOB.CONTENT column is always compressed using zlib. This @ -- column might hold the full text of the record or it might hold @ -- a delta that is able to reconstruct the record from some other @ -- record. If BLOB.CONTENT holds a delta, then a DELTA table entry @ -- will exist for the record and that entry will point to another @ -- entry that holds the source of the delta. Deltas can be chained. @ -- @ -- The blob and delta tables collectively hold the "global state" of @ -- a Fossil repository. @ -- @ CREATE TABLE blob( @ rid INTEGER PRIMARY KEY, -- Record ID @ rcvid INTEGER, -- Origin of this record @ size INTEGER, -- Size of content. -1 for a phantom. @ uuid TEXT UNIQUE NOT NULL, -- SHA1 hash of the content @ content BLOB, -- Compressed content of this record @ CHECK( length(uuid)==40 AND rid>0 ) @ ); @ CREATE TABLE delta( @ rid INTEGER PRIMARY KEY, -- BLOB that is delta-compressed @ srcid INTEGER NOT NULL REFERENCES blob -- Baseline for delta-compression @ ); @ CREATE INDEX delta_i1 ON delta(srcid); @ @ ------------------------------------------------------------------------- @ -- The BLOB and DELTA tables above hold the "global state" of a Fossil @ -- project; the stuff that is normally exchanged during "sync". The @ -- "local state" of a repository is contained in the remaining tables of @ -- the zRepositorySchema1 string. @ ------------------------------------------------------------------------- @ @ -- Whenever new blobs are received into the repository, an entry @ -- in this table records the source of the blob. @ -- @ CREATE TABLE rcvfrom( @ rcvid INTEGER PRIMARY KEY, -- Received-From ID |
︙ | ︙ | |||
123 124 125 126 127 128 129 | @ ipaddr TEXT, -- IP address for which cookie is valid @ cexpire DATETIME, -- Time when cookie expires @ info TEXT, -- contact information @ mtime DATE, -- last change. seconds since 1970 @ photo BLOB -- JPEG image of this user @ ); @ | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | @ ipaddr TEXT, -- IP address for which cookie is valid @ cexpire DATETIME, -- Time when cookie expires @ info TEXT, -- contact information @ mtime DATE, -- last change. seconds since 1970 @ photo BLOB -- JPEG image of this user @ ); @ @ -- The config table holds miscellanous information about the repository. @ -- in the form of name-value pairs. @ -- @ CREATE TABLE config( @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry @ value CLOB, -- Content of the named parameter @ mtime DATE, -- last modified. seconds since 1970 @ CHECK( typeof(name)='text' AND length(name)>=1 ) |
︙ | ︙ | |||
170 171 172 173 174 175 176 | @ cols TEXT, -- A color-key specification @ sqlcode TEXT -- An SQL SELECT statement for this report @ ); @ @ -- Some ticket content (such as the originators email address or contact @ -- information) needs to be obscured to protect privacy. This is achieved @ -- by storing an SHA1 hash of the content. For display, the hash is | | | | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | @ cols TEXT, -- A color-key specification @ sqlcode TEXT -- An SQL SELECT statement for this report @ ); @ @ -- Some ticket content (such as the originators email address or contact @ -- information) needs to be obscured to protect privacy. This is achieved @ -- by storing an SHA1 hash of the content. For display, the hash is @ -- mapped back into the original text using this table. @ -- @ -- This table contains sensitive information and should not be shared @ -- with unauthorized users. @ -- @ CREATE TABLE concealed( @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content @ mtime DATE, -- Time created. Seconds since 1970 @ content TEXT -- Content intended to be concealed @ ); @ @ -- The application ID helps the unix "file" command to identify the @ -- database as a fossil repository. @ PRAGMA application_id=252006673; ; /* ** The default reportfmt entry for the schema. This is in an extra ** script so that (configure reset) can install the default report. */ const char zRepositorySchemaDefaultReports[] = @ INSERT INTO reportfmt(title,mtime,cols,sqlcode) @ VALUES('All Tickets',julianday('1970-01-01'),'#ffffff Key: @ #f2dcdc Active @ #e8e8e8 Review @ #cfe8bd Fixed @ #bde5d6 Tested @ #cacae5 Deferred @ #c8c8c8 Closed','SELECT |
︙ | ︙ | |||
222 223 224 225 226 227 228 | @ -- Filenames @ -- @ CREATE TABLE filename( @ fnid INTEGER PRIMARY KEY, -- Filename ID @ name TEXT UNIQUE -- Name of file page @ ); @ | | > > > > > > > > > > > > > > > > > > | | > | > | < | > | > | | | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | @ -- Filenames @ -- @ CREATE TABLE filename( @ fnid INTEGER PRIMARY KEY, -- Filename ID @ name TEXT UNIQUE -- Name of file page @ ); @ @ -- Linkages between check-ins, files created by each check-in, and @ -- the names of those files. @ -- @ -- Each entry represents a file that changed content from pid to fid @ -- due to the check-in that goes from pmid to mid. fnid is the name @ -- of the file in the mid check-in. If the file was renamed as part @ -- of the mid check-in, then pfnid is the previous filename. @ @ -- There can be multiple entries for (mid,fid) if the mid check-in was @ -- a merge. Entries with isaux==0 are from the primary parent. Merge @ -- parents have isaux set to true. @ -- @ -- Field name mnemonics: @ -- mid = Manifest ID. (Each check-in is stored as a "Manifest") @ -- fid = File ID. @ -- pmid = Parent Manifest ID. @ -- pid = Parent file ID. @ -- fnid = File Name ID. @ -- pfnid = Parent File Name ID. @ -- isaux = pmid IS AUXiliary parent, not primary parent @ -- @ -- pid==0 if the file is added by check-in mid. @ -- fid==0 if the file is removed by check-in mid. @ -- @ CREATE TABLE mlink( @ mid INTEGER REFERENCES plink(cid), -- Check-in that contains fid @ fid INTEGER REFERENCES blob, -- New file content. 0 if deleted @ pmid INTEGER REFERENCES plink(cid), -- Check-in that contains pid @ pid INTEGER REFERENCES blob, -- Prev file content. 0 if new @ fnid INTEGER REFERENCES filename, -- Name of the file @ pfnid INTEGER REFERENCES filename, -- Previous name. 0 if unchanged @ mperm INTEGER, -- File permissions. 1==exec @ isaux BOOLEAN DEFAULT 0 -- TRUE if pmid is the primary @ ); @ CREATE INDEX mlink_i1 ON mlink(mid); @ CREATE INDEX mlink_i2 ON mlink(fnid); @ CREATE INDEX mlink_i3 ON mlink(fid); @ CREATE INDEX mlink_i4 ON mlink(pid); @ @ -- Parent/child linkages between check-ins @ -- @ CREATE TABLE plink( @ pid INTEGER REFERENCES blob, -- Parent manifest @ cid INTEGER REFERENCES blob, -- Child manifest @ isprim BOOLEAN, -- pid is the primary parent of cid @ mtime DATETIME, -- the date/time stamp on cid. Julian day. @ baseid INTEGER REFERENCES blob, -- Baseline if cid is a delta manifest. @ UNIQUE(pid, cid) @ ); @ CREATE INDEX plink_i2 ON plink(cid,pid); @ @ -- A "leaf" check-in is a check-in that has no children in the same @ -- branch. The set of all leaves is easily computed with a join, @ -- between the plink and tagxref tables, but it is a slower join for @ -- very large repositories (repositories with 100,000 or more check-ins) @ -- and so it makes sense to precompute the set of leaves. There is @ -- one entry in the following table for each leaf. @ -- @ CREATE TABLE leaf(rid INTEGER PRIMARY KEY); @ @ -- Events used to generate a timeline @ -- |
︙ | ︙ | |||
319 320 321 322 323 324 325 | @ -- @ CREATE TABLE unsent( @ rid INTEGER PRIMARY KEY -- Record ID of the phantom @ ); @ @ -- Each baseline or manifest can have one or more tags. A tag @ -- is defined by a row in the next table. | | | | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | @ -- @ CREATE TABLE unsent( @ rid INTEGER PRIMARY KEY -- Record ID of the phantom @ ); @ @ -- Each baseline or manifest can have one or more tags. A tag @ -- is defined by a row in the next table. @ -- @ -- Wiki pages are tagged with "wiki-NAME" where NAME is the name of @ -- the wiki page. Tickets changes are tagged with "ticket-UUID" where @ -- UUID is the indentifier of the ticket. Tags used to assign symbolic @ -- names to baselines are branches are of the form "sym-NAME" where @ -- NAME is the symbolic name. @ -- @ CREATE TABLE tag( @ tagid INTEGER PRIMARY KEY, -- Numeric tag ID @ tagname TEXT UNIQUE -- Tag name. |
︙ | ︙ | |||
366 367 368 369 370 371 372 | @ -- when a check-in comment refers to a ticket) an entry is made in @ -- the following table for that hyperlink. This table is used to @ -- facilitate the display of "back links". @ -- @ CREATE TABLE backlink( @ target TEXT, -- Where the hyperlink points to @ srctype INT, -- 0: check-in 1: ticket 2: wiki | | | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | @ -- when a check-in comment refers to a ticket) an entry is made in @ -- the following table for that hyperlink. This table is used to @ -- facilitate the display of "back links". @ -- @ CREATE TABLE backlink( @ target TEXT, -- Where the hyperlink points to @ srctype INT, -- 0: check-in 1: ticket 2: wiki @ srcid INT, -- rid for check-in or wiki. tkt_id for ticket. @ mtime TIMESTAMP, -- time that the hyperlink was added. Julian day. @ UNIQUE(target, srctype, srcid) @ ); @ CREATE INDEX backlink_src ON backlink(srcid, srctype); @ @ -- Each attachment is an entry in the following table. Only @ -- the most recent attachment (identified by the D card) is saved. |
︙ | ︙ | |||
433 434 435 436 437 438 439 | ** Predefined tagid values */ #if INTERFACE # define TAG_BGCOLOR 1 /* Set the background color for display */ # define TAG_COMMENT 2 /* The check-in comment */ # define TAG_USER 3 /* User who made a checking */ # define TAG_DATE 4 /* The date of a check-in */ | | | | | | | 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 | ** Predefined tagid values */ #if INTERFACE # define TAG_BGCOLOR 1 /* Set the background color for display */ # define TAG_COMMENT 2 /* The check-in comment */ # define TAG_USER 3 /* User who made a checking */ # define TAG_DATE 4 /* The date of a check-in */ # define TAG_HIDDEN 5 /* Do not display in timeline */ # define TAG_PRIVATE 6 /* Do not sync */ # define TAG_CLUSTER 7 /* A cluster */ # define TAG_BRANCH 8 /* Value is name of the current branch */ # define TAG_CLOSED 9 /* Do not display this check-in as a leaf */ # define TAG_PARENT 10 /* Change to parentage on a check-in */ #endif #if EXPORT_INTERFACE # define MAX_INT_TAG 16 /* The largest pre-assigned tag id */ #endif /* ** The schema for the local FOSSIL database file found at the root ** of every check-out. This database contains the complete state of ** the checkout. */ const char zLocalSchema[] = @ -- The VVAR table holds miscellanous information about the local database @ -- in the form of name-value pairs. This is similar to the VAR table @ -- table in the repository except that this table holds information that @ -- is specific to the local checkout. |
︙ | ︙ | |||
477 478 479 480 481 482 483 | @ -- added but not yet committed. @ -- @ -- Vfile.chnged is 0 for unmodified files, 1 for files that have @ -- been edited or which have been subjected to a 3-way merge. @ -- Vfile.chnged is 2 if the file has been replaced from a different @ -- version by the merge and 3 if the file has been added by a merge. @ -- Vfile.chnged is 4|5 is the same as 2|3, but the operation has been | | | | | 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | @ -- added but not yet committed. @ -- @ -- Vfile.chnged is 0 for unmodified files, 1 for files that have @ -- been edited or which have been subjected to a 3-way merge. @ -- Vfile.chnged is 2 if the file has been replaced from a different @ -- version by the merge and 3 if the file has been added by a merge. @ -- Vfile.chnged is 4|5 is the same as 2|3, but the operation has been @ -- done by an --integrate merge. The difference between vfile.chnged==3|5 @ -- and a regular add is that with vfile.chnged==3|5 we know that the @ -- current version of the file is already in the repository. @ -- @ CREATE TABLE vfile( @ id INTEGER PRIMARY KEY, -- ID of the checked out file @ vid INTEGER REFERENCES blob, -- The baseline this file is part of. @ chnged INT DEFAULT 0, -- 0:unchng 1:edit 2:m-chng 3:m-add 4:i-chng 5:i-add @ deleted BOOLEAN DEFAULT 0, -- True if deleted @ isexe BOOLEAN, -- True if file should be executable @ islink BOOLEAN, -- True if file should be symlink @ rid INTEGER, -- Originally from this repository record @ mrid INTEGER, -- Based on this record due to a merge @ mtime INTEGER, -- Mtime of file on disk. sec since 1970 @ pathname TEXT, -- Full pathname relative to root |
︙ | ︙ |
Changes to src/search.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** | | > | > > > > > > > > | | | | | | > > > > > > > > > > > > < < < < < < < | < < < < < < < < < < < < < < < < < < < | < | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement a very simple search function ** against timeline comments, check-in content, wiki pages, and/or tickets. ** ** The search is full-text like in that it is looking for words and ignores ** punctuation and capitalization. But it is more akin to "grep" in that ** it scans the entire corpus for the search, and it does not support the ** full functionality of FTS4. */ #include "config.h" #include "search.h" #include <assert.h> #if INTERFACE /* Maximum number of search terms */ #define SEARCH_MAX_TERM 8 /* ** A compiled search pattern */ struct Search { int nTerm; /* Number of search terms */ struct srchTerm { /* For each search term */ char *z; /* Text */ int n; /* length */ } a[SEARCH_MAX_TERM]; /* Snippet controls */ char *zPattern; /* The search pattern */ char *zMarkBegin; /* Start of a match */ char *zMarkEnd; /* End of a match */ char *zMarkGap; /* A gap between two matches */ unsigned fSrchFlg; /* Flags */ int iScore; /* Score of the last match attempt */ Blob snip; /* Snippet for the most recent match */ }; #define SRCHFLG_HTML 0x01 /* Escape snippet text for HTML */ #define SRCHFLG_STATIC 0x04 /* The static gSearch object */ #endif /* ** There is a single global Search object: */ static Search gSearch; /* ** Theses characters constitute a word boundary */ static const char isBoundary[] = { 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, |
︙ | ︙ | |||
85 86 87 88 89 90 91 92 93 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > < | | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > | | | | < | | < | < | < | > | > | | > > > | < > > > > | | | < > | > > > > | < > > > > < > > > | > > > > > > > > > > > > > > > > > > > | > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > > > > > > > > > > > | | > > | < | > | | > > > > > > > > > | | | | | > > | < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #define ISALNUM(x) (!isBoundary[(x)&0xff]) /* ** Destroy a search context. */ void search_end(Search *p){ if( p ){ fossil_free(p->zPattern); fossil_free(p->zMarkBegin); fossil_free(p->zMarkEnd); fossil_free(p->zMarkGap); if( p->iScore ) blob_reset(&p->snip); memset(p, 0, sizeof(*p)); if( p!=&gSearch ) fossil_free(p); } } /* ** Compile a search pattern */ Search *search_init( const char *zPattern, /* The search pattern */ const char *zMarkBegin, /* Start of a match */ const char *zMarkEnd, /* End of a match */ const char *zMarkGap, /* A gap between two matches */ unsigned fSrchFlg /* Flags */ ){ Search *p; char *z; int i; if( fSrchFlg & SRCHFLG_STATIC ){ p = &gSearch; search_end(p); }else{ p = fossil_malloc(sizeof(*p)); memset(p, 0, sizeof(*p)); } p->zPattern = z = mprintf("%s", zPattern); p->zMarkBegin = mprintf("%s", zMarkBegin); p->zMarkEnd = mprintf("%s", zMarkEnd); p->zMarkGap = mprintf("%s", zMarkGap); p->fSrchFlg = fSrchFlg; blob_init(&p->snip, 0, 0); while( *z && p->nTerm<SEARCH_MAX_TERM ){ while( *z && !ISALNUM(*z) ){ z++; } if( *z==0 ) break; p->a[p->nTerm].z = z; for(i=1; ISALNUM(z[i]); i++){} p->a[p->nTerm].n = i; z += i; p->nTerm++; } return p; } /* ** Append n bytes of text to snippet zTxt. Encode the text appropriately. */ static void snippet_text_append( Search *p, /* The search context */ Blob *pSnip, /* Append to this snippet */ const char *zTxt, /* Text to append */ int n /* How many bytes to append */ ){ if( n>0 ){ if( p->fSrchFlg & SRCHFLG_HTML ){ blob_appendf(pSnip, "%#h", n, zTxt); }else{ blob_append(pSnip, zTxt, n); } } } /* ** Compare a search pattern against one or more input strings which ** collectively comprise a document. Return a match score. Any ** postive value means there was a match. Zero means that one or ** more terms are missing. ** ** The score and a snippet are record for future use. ** ** Scoring: ** * All terms must match at least once or the score is zero ** * One point for each matching term ** * Extra points if consecutive words of the pattern are consecutive ** in the document */ static int search_match( Search *p, /* Search pattern and flags */ int nDoc, /* Number of strings in this document */ const char **azDoc /* Text of each string */ ){ int score; /* Final score */ int i; /* Offset into current document */ int ii; /* Loop counter */ int j; /* Loop over search terms */ int k; /* Loop over prior terms */ int iWord = 0; /* Current word number */ int iDoc; /* Current document number */ int wantGap = 0; /* True if a zMarkGap is wanted */ const char *zDoc; /* Current document text */ const int CTX = 50; /* Amount of snippet context */ int anMatch[SEARCH_MAX_TERM]; /* Number of terms in best match */ int aiBestDoc[SEARCH_MAX_TERM]; /* Document containing best match */ int aiBestOfst[SEARCH_MAX_TERM]; /* Byte offset to start of best match */ int aiLastDoc[SEARCH_MAX_TERM]; /* Document containing most recent match */ int aiLastOfst[SEARCH_MAX_TERM]; /* Byte offset to the most recent match */ int aiWordIdx[SEARCH_MAX_TERM]; /* Word index of most recent match */ memset(anMatch, 0, sizeof(anMatch)); memset(aiWordIdx, 0xff, sizeof(aiWordIdx)); for(iDoc=0; iDoc<nDoc; iDoc++){ zDoc = azDoc[iDoc]; if( zDoc==0 ) continue; iWord++; for(i=0; zDoc[i]; i++){ if( !ISALNUM(zDoc[i]) ) continue; iWord++; for(j=0; j<p->nTerm; j++){ int n = p->a[j].n; if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 && (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*') ){ aiWordIdx[j] = iWord; aiLastDoc[j] = iDoc; aiLastOfst[j] = i; for(k=1; j-k>=0 && anMatch[j-k] && aiWordIdx[j-k]==iWord-k; k++){} for(ii=0; ii<k; ii++){ if( anMatch[j-ii]<k ){ anMatch[j-ii] = k*(nDoc-iDoc); aiBestDoc[j-ii] = aiLastDoc[j-ii]; aiBestOfst[j-ii] = aiLastOfst[j-ii]; } } break; } } while( ISALNUM(zDoc[i]) ){ i++; } if( zDoc[i]==0 ) break; } } /* Finished search all documents. ** Every term must be seen or else the score is zero */ score = 1; for(j=0; j<p->nTerm; j++) score *= anMatch[j]; blob_reset(&p->snip); p->iScore = score; if( score==0 ) return score; /* Prepare a snippet that describes the matching text. */ while(1){ int iOfst; int iTail; int iBest; for(ii=0; ii<p->nTerm && anMatch[ii]==0; ii++){} if( ii>=p->nTerm ) break; /* This is where the loop exits */ iBest = ii; iDoc = aiBestDoc[ii]; iOfst = aiBestOfst[ii]; for(; ii<p->nTerm; ii++){ if( anMatch[ii]==0 ) continue; if( aiBestDoc[ii]>iDoc ) continue; if( aiBestOfst[ii]>iOfst ) continue; iDoc = aiBestDoc[ii]; iOfst = aiBestOfst[ii]; iBest = ii; } iTail = iOfst + p->a[iBest].n; anMatch[iBest] = 0; for(ii=0; ii<p->nTerm; ii++){ if( anMatch[ii]==0 ) continue; if( aiBestDoc[ii]!=iDoc ) continue; if( aiBestOfst[ii]<=iTail+CTX*2 ){ if( iTail<aiBestOfst[ii]+p->a[ii].n ){ iTail = aiBestOfst[ii]+p->a[ii].n; } anMatch[ii] = 0; ii = -1; continue; } } zDoc = azDoc[iDoc]; iOfst -= CTX; if( iOfst<0 ) iOfst = 0; while( iOfst>0 && ISALNUM(zDoc[iOfst-1]) ) iOfst--; while( zDoc[iOfst] && !ISALNUM(zDoc[iOfst]) ) iOfst++; for(ii=0; ii<CTX && zDoc[iTail]; ii++, iTail++){} while( ISALNUM(zDoc[iTail]) ) iTail++; if( iOfst>0 || wantGap ) blob_append(&p->snip, p->zMarkGap, -1); wantGap = zDoc[iTail]!=0; zDoc += iOfst; iTail -= iOfst; /* Add a snippet segment using characters iOfst..iOfst+iTail from zDoc */ for(i=0; i<iTail; i++){ if( !ISALNUM(zDoc[i]) ) continue; for(j=0; j<p->nTerm; j++){ int n = p->a[j].n; if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 && (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*') ){ snippet_text_append(p, &p->snip, zDoc, i); zDoc += i; iTail -= i; blob_append(&p->snip, p->zMarkBegin, -1); if( p->a[j].z[n]=='*' ){ while( ISALNUM(zDoc[n]) ) n++; } snippet_text_append(p, &p->snip, zDoc, n); zDoc += n; iTail -= n; blob_append(&p->snip, p->zMarkEnd, -1); i = -1; break; } /* end-if */ } /* end for(j) */ if( j<p->nTerm ){ while( ISALNUM(zDoc[i]) && i<iTail ){ i++; } } } /* end for(i) */ snippet_text_append(p, &p->snip, zDoc, iTail); } if( wantGap ) blob_append(&p->snip, p->zMarkGap, -1); return score; } /* ** COMMAND: test-match ** ** Usage: fossil test-match SEARCHSTRING FILE1 FILE2 ... */ void test_match_cmd(void){ Search *p; int i; Blob x; int score; char *zDoc; int flg = 0; char *zBegin = (char*)find_option("begin",0,1); char *zEnd = (char*)find_option("end",0,1); char *zGap = (char*)find_option("gap",0,1); if( find_option("html",0,0)!=0 ) flg |= SRCHFLG_HTML; if( find_option("static",0,0)!=0 ) flg |= SRCHFLG_STATIC; verify_all_options(); if( g.argc<4 ) usage("SEARCHSTRING FILE1..."); if( zBegin==0 ) zBegin = "[["; if( zEnd==0 ) zEnd = "]]"; if( zGap==0 ) zGap = " ... "; p = search_init(g.argv[2], zBegin, zEnd, zGap, flg); for(i=3; i<g.argc; i++){ blob_read_from_file(&x, g.argv[i]); zDoc = blob_str(&x); score = search_match(p, 1, (const char**)&zDoc); fossil_print("%s: %d\n", g.argv[i], p->iScore); blob_reset(&x); if( score ){ fossil_print("%.78c\n%s\n%.78c\n\n", '=', blob_str(&p->snip), '='); } } search_end(p); } /* ** An SQL function to initialize the global search pattern: ** ** search_init(PATTERN,BEGIN,END,GAP,FLAGS) ** ** All arguments are optional. */ static void search_init_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zPattern = 0; const char *zBegin = "<mark>"; const char *zEnd = "</mark>"; const char *zGap = " ... "; unsigned int flg = SRCHFLG_HTML; switch( argc ){ default: flg = (unsigned int)sqlite3_value_int(argv[4]); case 4: zGap = (const char*)sqlite3_value_text(argv[3]); case 3: zEnd = (const char*)sqlite3_value_text(argv[2]); case 2: zBegin = (const char*)sqlite3_value_text(argv[1]); case 1: zPattern = (const char*)sqlite3_value_text(argv[0]); } if( zPattern && zPattern[0] ){ search_init(zPattern, zBegin, zEnd, zGap, flg | SRCHFLG_STATIC); }else{ search_end(&gSearch); } } /* ** Try to match the input text against the search parameters set up ** by the previous search_init() call. Remember the results globally. ** Return non-zero on a match and zero on a miss. */ static void search_match_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *azDoc[5]; int nDoc; int rc; for(nDoc=0; nDoc<ArraySize(azDoc) && nDoc<argc; nDoc++){ azDoc[nDoc] = (const char*)sqlite3_value_text(argv[nDoc]); if( azDoc[nDoc]==0 ) azDoc[nDoc] = ""; } rc = search_match(&gSearch, nDoc, azDoc); sqlite3_result_int(context, rc); } /* ** These SQL functions return the results of the last ** call to the search_match() SQL function. */ static void search_score_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3_result_int(context, gSearch.iScore); } static void search_snippet_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ if( blob_size(&gSearch.snip)>0 ){ sqlite3_result_text(context, blob_str(&gSearch.snip), -1, fossil_free); blob_init(&gSearch.snip, 0, 0); } } /* ** This is an SQLite function that computes the searchable text. ** It is a wrapper around the search_stext() routine. See the ** search_stext() routine for further detail. */ static void search_stext_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zType = (const char*)sqlite3_value_text(argv[0]); int rid = sqlite3_value_int(argv[1]); const char *zName = (const char*)sqlite3_value_text(argv[2]); sqlite3_result_text(context, search_stext_cached(zType[0],rid,zName,0), -1, SQLITE_TRANSIENT); } static void search_title_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zType = (const char*)sqlite3_value_text(argv[0]); int rid = sqlite3_value_int(argv[1]); const char *zName = (const char*)sqlite3_value_text(argv[2]); int nHdr; char *z = search_stext_cached(zType[0], rid, zName, &nHdr); if( nHdr || zType[0]!='d' ){ sqlite3_result_text(context, z, nHdr, SQLITE_TRANSIENT); }else{ sqlite3_result_value(context, argv[2]); } } static void search_body_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zType = (const char*)sqlite3_value_text(argv[0]); int rid = sqlite3_value_int(argv[1]); const char *zName = (const char*)sqlite3_value_text(argv[2]); int nHdr; char *z = search_stext_cached(zType[0], rid, zName, &nHdr); sqlite3_result_text(context, z+nHdr+1, -1, SQLITE_TRANSIENT); } /* ** Encode a string for use as a query parameter in a URL */ static void search_urlencode_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ char *z = mprintf("%T",sqlite3_value_text(argv[0])); sqlite3_result_text(context, z, -1, fossil_free); } /* ** Register the "score()" SQL function to score its input text ** using the given Search object. Once this function is registered, ** do not delete the Search object. */ void search_sql_setup(sqlite3 *db){ static int once = 0; if( once++ ) return; sqlite3_create_function(db, "search_match", -1, SQLITE_UTF8, 0, search_match_sqlfunc, 0, 0); sqlite3_create_function(db, "search_score", 0, SQLITE_UTF8, 0, search_score_sqlfunc, 0, 0); sqlite3_create_function(db, "search_snippet", 0, SQLITE_UTF8, 0, search_snippet_sqlfunc, 0, 0); sqlite3_create_function(db, "search_init", -1, SQLITE_UTF8, 0, search_init_sqlfunc, 0, 0); sqlite3_create_function(db, "stext", 3, SQLITE_UTF8, 0, search_stext_sqlfunc, 0, 0); sqlite3_create_function(db, "title", 3, SQLITE_UTF8, 0, search_title_sqlfunc, 0, 0); sqlite3_create_function(db, "body", 3, SQLITE_UTF8, 0, search_body_sqlfunc, 0, 0); sqlite3_create_function(db, "urlencode", 1, SQLITE_UTF8, 0, search_urlencode_sqlfunc, 0, 0); } /* ** Testing the search function. ** ** COMMAND: search* ** %fossil search [-all|-a] [-limit|-n #] [-width|-W #] pattern... ** ** Search for timeline entries matching all words ** provided on the command line. Whole-word matches ** scope more highly than partial matches. ** ** Outputs, by default, some top-N fraction of the ** results. The -all option can be used to output ** all matches, regardless of their search score. ** The -limit option can be used to limit the number ** of entries returned. The -width option can be ** used to set the output width used when printing ** matches. */ void search_cmd(void){ Blob pattern; int i; Blob sql = empty_blob; Stmt q; int iBest; char fAll = NULL != find_option("all", "a", 0); /* If set, do not lop off the end of the results. */ const char *zLimit = find_option("limit","n",1); const char *zWidth = find_option("width","W",1); int nLimit = zLimit ? atoi(zLimit) : -1000; /* Max number of matching lines/entries to list */ int width; if( zWidth ){ width = atoi(zWidth); if( (width!=0) && (width<=20) ){ fossil_fatal("-W|--width value must be >20 or 0"); } }else{ width = -1; } db_must_be_within_tree(); if( g.argc<2 ) return; blob_init(&pattern, g.argv[2], -1); for(i=3; i<g.argc; i++){ blob_appendf(&pattern, " %s", g.argv[i]); } (void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC); blob_reset(&pattern); search_sql_setup(g.db); db_multi_exec( "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);" "CREATE INDEX srch_idx1 ON srch(x);" "INSERT INTO srch(rid,uuid,date,comment,x)" " SELECT blob.rid, uuid, datetime(event.mtime%s)," " coalesce(ecomment,comment)," " search_score()" " FROM event, blob" " WHERE blob.rid=event.objid" " AND search_match(coalesce(ecomment,comment));", timeline_utc() ); iBest = db_int(0, "SELECT max(x) FROM srch"); blob_append(&sql, "SELECT rid, uuid, date, comment, 0, 0 FROM srch " "WHERE 1 ", -1); if(!fAll){ blob_append_sql(&sql,"AND x>%d ", iBest/3); } blob_append(&sql, "ORDER BY x DESC, date DESC ", -1); db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); print_timeline(&q, nLimit, width, 0); db_finalize(&q); } #if INTERFACE /* What to search for */ #define SRCH_CKIN 0x0001 /* Search over check-in comments */ #define SRCH_DOC 0x0002 /* Search over embedded documents */ #define SRCH_TKT 0x0004 /* Search over tickets */ #define SRCH_WIKI 0x0008 /* Search over wiki */ #define SRCH_ALL 0x000f /* Search over everything */ #endif /* ** Remove bits from srchFlags which are disallowed by either the ** current server configuration or by user permissions. */ unsigned int search_restrict(unsigned int srchFlags){ static unsigned int knownGood = 0; static unsigned int knownBad = 0; static const struct { unsigned m; const char *zKey; } aSetng[] = { { SRCH_CKIN, "search-ci" }, { SRCH_DOC, "search-doc" }, { SRCH_TKT, "search-tkt" }, { SRCH_WIKI, "search-wiki" }, }; int i; if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC); if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT); if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI); for(i=0; i<ArraySize(aSetng); i++){ unsigned int m = aSetng[i].m; if( (srchFlags & m)==0 ) continue; if( ((knownGood|knownBad) & m)!=0 ) continue; if( db_get_boolean(aSetng[i].zKey,0) ){ knownGood |= m; }else{ knownBad |= m; } } return srchFlags & ~knownBad; } /* ** When this routine is called, there already exists a table ** ** x(label,url,score,date,snip). ** ** And the srchFlags parameter has been validated. This routine ** fills the X table with search results using a full-text scan. ** ** The companion indexed scan routine is search_indexed(). */ static void search_fullscan( const char *zPattern, /* The query pattern */ unsigned int srchFlags /* What to search over */ ){ search_init(zPattern, "<mark>", "</mark>", " ... ", SRCHFLG_STATIC|SRCHFLG_HTML); if( (srchFlags & SRCH_DOC)!=0 ){ char *zDocGlob = db_get("doc-glob",""); char *zDocBr = db_get("doc-branch","trunk"); if( zDocGlob && zDocGlob[0] && zDocBr && zDocBr[0] ){ db_multi_exec( "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" ); db_multi_exec( "INSERT INTO x(label,url,score,id,date,snip)" " SELECT printf('Document: %%s',title('d',blob.rid,foci.filename))," " printf('/doc/%T/%%s',foci.filename)," " search_score()," " 'd'||blob.rid," " (SELECT datetime(event.mtime) FROM event" " WHERE objid=symbolic_name_to_rid('trunk'))," " search_snippet()" " FROM foci CROSS JOIN blob" " WHERE checkinID=symbolic_name_to_rid('trunk')" " AND blob.uuid=foci.uuid" " AND search_match(title('d',blob.rid,foci.filename)," " body('d',blob.rid,foci.filename))" " AND %z", zDocBr, glob_expr("foci.filename", zDocGlob) ); } } if( (srchFlags & SRCH_WIKI)!=0 ){ db_multi_exec( "WITH wiki(name,rid,mtime) AS (" " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)" " FROM tag, tagxref" " WHERE tag.tagname GLOB 'wiki-*'" " AND tagxref.tagid=tag.tagid" " GROUP BY 1" ")" "INSERT INTO x(label,url,score,id,date,snip)" " SELECT printf('Wiki: %%s',name)," " printf('/wiki?name=%%s',urlencode(name))," " search_score()," " 'w'||rid," " datetime(mtime)," " search_snippet()" " FROM wiki" " WHERE search_match(title('w',rid,name),body('w',rid,name));" ); } if( (srchFlags & SRCH_CKIN)!=0 ){ db_multi_exec( "WITH ckin(uuid,rid,mtime) AS (" " SELECT blob.uuid, event.objid, event.mtime" " FROM event, blob" " WHERE event.type='ci'" " AND blob.rid=event.objid" ")" "INSERT INTO x(label,url,score,id,date,snip)" " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime))," " printf('/timeline?c=%%s&n=8&y=ci',uuid)," " search_score()," " 'c'||rid," " datetime(mtime)," " search_snippet()" " FROM ckin" " WHERE search_match('',body('c',rid,NULL));" ); } if( (srchFlags & SRCH_TKT)!=0 ){ db_multi_exec( "INSERT INTO x(label,url,score,id,date,snip)" " SELECT printf('Ticket: %%s (%%s)',title('t',tkt_id,NULL)," "datetime(tkt_mtime))," " printf('/tktview/%%.20s',tkt_uuid)," " search_score()," " 't'||tkt_id," " datetime(tkt_mtime)," " search_snippet()" " FROM ticket" " WHERE search_match(title('t',tkt_id,NULL),body('t',tkt_id,NULL));" ); } } /* ** Number of significant bits in a u32 */ static int nbits(u32 x){ int n = 0; while( x ){ n++; x >>= 1; } return n; } /* ** Implemenation of the rank() function used with rank(matchinfo(*,'pcsx')). */ static void search_rank_sqlfunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const unsigned *aVal = (unsigned int*)sqlite3_value_blob(argv[0]); int nVal = sqlite3_value_bytes(argv[0])/4; int nCol; /* Number of columns in the index */ int nTerm; /* Number of search terms in the query */ int i, j; /* Loop counter */ double r = 0.0; /* Score */ const unsigned *aX, *aS; if( nVal<2 ) return; nTerm = aVal[0]; nCol = aVal[1]; if( nVal<2+3*nCol*nTerm+nCol ) return; aS = aVal+2; aX = aS+nCol; for(j=0; j<nCol; j++){ double x; if( aS[j]>0 ){ x = 0.0; for(i=0; i<nTerm; i++){ int hits_this_row; int hits_all_rows; int rows_with_hit; double avg_hits_per_row; hits_this_row = aX[j + i*nCol*3]; if( hits_this_row==0 )continue; hits_all_rows = aX[j + i*nCol*3 + 1]; rows_with_hit = aX[j + i*nCol*3 + 2]; if( rows_with_hit==0 ) continue; avg_hits_per_row = hits_all_rows/(double)rows_with_hit; x += hits_this_row/(avg_hits_per_row*nbits(rows_with_hit)); } x *= (1<<((30*(aS[j]-1))/nTerm)); }else{ x = 0.0; } r = r*10.0 + x; } #define SEARCH_DEBUG_RANK 0 #if SEARCH_DEBUG_RANK { Blob x; blob_init(&x,0,0); blob_appendf(&x,"%08x", (int)r); for(i=0; i<nVal; i++){ blob_appendf(&x," %d", aVal[i]); } blob_appendf(&x," r=%g", r); sqlite3_result_text(context, blob_str(&x), -1, fossil_free); } #else sqlite3_result_double(context, r); #endif } /* ** When this routine is called, there already exists a table ** ** x(label,url,score,date,snip). ** ** And the srchFlags parameter has been validated. This routine ** fills the X table with search results using a index scan. ** ** The companion full-text scan routine is search_fullscan(). */ static void search_indexed( const char *zPattern, /* The query pattern */ unsigned int srchFlags /* What to search over */ ){ Blob sql; if( srchFlags==0 ) return; sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8, 0, search_rank_sqlfunc, 0, 0); blob_init(&sql, 0, 0); blob_appendf(&sql, "INSERT INTO x(label,url,score,id,date,snip) " " SELECT ftsdocs.label," " ftsdocs.url," " rank(matchinfo(ftsidx,'pcsx'))," " ftsdocs.type || ftsdocs.rid," " datetime(ftsdocs.mtime)," " snippet(ftsidx,'<mark>','</mark>',' ... ',-1,35)" " FROM ftsidx CROSS JOIN ftsdocs" " WHERE ftsidx MATCH %Q" " AND ftsdocs.rowid=ftsidx.docid", zPattern ); if( srchFlags!=SRCH_ALL ){ const char *zSep = " AND ("; static const struct { unsigned m; char c; } aMask[] = { { SRCH_CKIN, 'c' }, { SRCH_DOC, 'd' }, { SRCH_TKT, 't' }, { SRCH_WIKI, 'w' }, }; int i; for(i=0; i<ArraySize(aMask); i++){ if( srchFlags & aMask[i].m ){ blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c); zSep = " OR "; } } blob_append(&sql,")",1); } db_multi_exec("%s",blob_str(&sql)/*safe-for-%s*/); #if SEARCH_DEBUG_RANK db_multi_exec("UPDATE x SET label=printf('%%s (score=%%s)',label,score)"); #endif } /* ** If z[] is of the form "<mark>TEXT</mark>" where TEXT contains ** no white-space or punctuation, then return the length of the mark. */ static int isSnippetMark(const char *z){ int n; if( strncmp(z,"<mark>",6)!=0 ) return 0; n = 6; while( fossil_isalnum(z[n]) ) n++; if( strncmp(&z[n],"</mark>",7)!=0 ) return 0; return n+7; } /* ** Return a copy of zSnip (in memory obtained from fossil_malloc()) that ** has all "<" characters, other than those on <mark> and </mark>, ** converted into "<". This is similar to htmlize() except that ** <mark> and </mark> are preserved. */ static char *cleanSnippet(const char *zSnip){ int i; int n = 0; char *z; for(i=0; zSnip[i]; i++) if( zSnip[i]=='<' ) n++; z = fossil_malloc( i+n*4+1 ); i = 0; while( zSnip[0] ){ if( zSnip[0]=='<' ){ n = isSnippetMark(zSnip); if( n ){ memcpy(&z[i], zSnip, n); zSnip += n; i += n; continue; }else{ memcpy(&z[i], "<", 4); i += 4; zSnip++; } }else{ z[i++] = zSnip[0]; zSnip++; } } z[i] = 0; return z; } /* ** This routine generates web-page output for a search operation. ** Other web-pages can invoke this routine to add search results ** in the middle of the page. ** ** Return the number of rows. */ int search_run_and_output( const char *zPattern, /* The query pattern */ unsigned int srchFlags, /* What to search over */ int fDebug /* Extra debugging output */ ){ Stmt q; int nRow = 0; srchFlags = search_restrict(srchFlags); if( srchFlags==0 ) return 0; search_sql_setup(g.db); add_content_sql_commands(g.db); db_multi_exec( "CREATE TEMP TABLE x(label,url,score,id,date,snip);" ); if( !search_index_exists() ){ search_fullscan(zPattern, srchFlags); }else{ search_update_index(srchFlags); search_indexed(zPattern, srchFlags); } db_prepare(&q, "SELECT url, snip, label, score, id" " FROM x" " ORDER BY score DESC, date DESC;"); while( db_step(&q)==SQLITE_ROW ){ const char *zUrl = db_column_text(&q, 0); const char *zSnippet = db_column_text(&q, 1); const char *zLabel = db_column_text(&q, 2); if( nRow==0 ){ @ <ol> } nRow++; @ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a> if( fDebug ){ @ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4))) } @ <br><span class='snippet'>%z(cleanSnippet(zSnippet))</span></li> } db_finalize(&q); if( nRow ){ @ </ol> } return nRow; } /* ** Generate some HTML for doing search. At a minimum include the ** Search-Text entry form. If the "s" query parameter is present, also ** show search results. ** ** The srchFlags parameter restricts the set of documents to be searched. ** srchFlags should normally be either a single search category or all ** categories. Any srchFlags with two or more bits set ** is treated like SRCH_ALL for display purposes. ** ** This routine automatically restricts srchFlag according to user ** permissions and the server configuration. The entry box is shown ** disabled if srchFlags is 0 after these restrictions are applied. ** ** If useYparam is true, then this routine also looks at the y= query ** parameter for further search restrictions. */ void search_screen(unsigned srchFlags, int useYparam){ const char *zType = 0; const char *zClass = 0; const char *zDisable1; const char *zDisable2; const char *zPattern; int fDebug = PB("debug"); srchFlags = search_restrict(srchFlags); switch( srchFlags ){ case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break; case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break; case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break; case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break; } if( srchFlags==0 ){ zDisable1 = " disabled"; zDisable2 = " disabled"; zPattern = ""; }else{ zDisable1 = " autofocus"; zDisable2 = ""; zPattern = PD("s",""); } @ <form method='GET' action='%R/%t(g.zPath)'> if( zClass ){ @ <div class='searchForm searchForm%s(zClass)'> }else{ @ <div class='searchForm'> } @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)> if( useYparam && (srchFlags & (srchFlags-1))!=0 && useYparam ){ static const struct { char *z; char *zNm; unsigned m; } aY[] = { { "all", "All", SRCH_ALL }, { "c", "Check-ins", SRCH_CKIN }, { "d", "Docs", SRCH_DOC }, { "t", "Tickets", SRCH_TKT }, { "w", "Wiki", SRCH_WIKI }, }; const char *zY = PD("y","all"); unsigned newFlags = srchFlags; int i; @ <select size='1' name='y'> for(i=0; i<ArraySize(aY); i++){ if( (aY[i].m & srchFlags)==0 ) continue; cgi_printf("<option value='%s'", aY[i].z); if( fossil_strcmp(zY,aY[i].z)==0 ){ newFlags &= aY[i].m; cgi_printf(" selected"); } cgi_printf(">%s</option>\n", aY[i].zNm); } @ </select> srchFlags = newFlags; } if( fDebug ){ @ <input type="hidden" name="debug" value="1"> } @ <input type="submit" value="Search%s(zType)"%s(zDisable2)> if( srchFlags==0 ){ @ <p class="generalError">Search is disabled</p> } @ </div></form> while( fossil_isspace(zPattern[0]) ) zPattern++; if( zPattern[0] ){ if( zClass ){ @ <div class='searchResult searchResult%s(zClass)'> }else{ @ <div class='searchResult'> } if( search_run_and_output(zPattern, srchFlags, fDebug)==0 ){ @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p> } @ </div> } } /* ** WEBPAGE: /search ** ** Search for check-in comments, documents, tickets, or wiki that ** match a user-supplied pattern. */ void search_page(void){ login_check_credentials(); style_header("Search"); search_screen(SRCH_ALL, 1); style_footer(); } /* ** This is a helper function for search_stext(). Writing into pOut ** the search text obtained from pIn according to zMimetype. ** ** The title of the document is the first line of text. All subsequent ** lines are the body. If the document has no title, the first line ** is blank. */ static void get_stext_by_mimetype( Blob *pIn, const char *zMimetype, Blob *pOut ){ Blob html, title; blob_init(&html, 0, 0); blob_init(&title, 0, 0); if( zMimetype==0 ) zMimetype = "text/plain"; if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){ Blob tail; blob_init(&tail, 0, 0); if( wiki_find_title(pIn, &title, &tail) ){ blob_appendf(pOut, "%s\n", blob_str(&title)); wiki_convert(&tail, &html, 0); blob_reset(&tail); }else{ blob_append(pOut, "\n", 1); wiki_convert(pIn, &html, 0); } html_to_plaintext(blob_str(&html), pOut); }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){ markdown_to_html(pIn, &title, &html); if( blob_size(&title) ){ blob_appendf(pOut, "%s\n", blob_str(&title)); }else{ blob_append(pOut, "\n", 1); } html_to_plaintext(blob_str(&html), pOut); }else if( fossil_strcmp(zMimetype,"text/html")==0 ){ if( doc_is_embedded_html(pIn, &title) ){ blob_appendf(pOut, "%s\n", blob_str(&title)); } html_to_plaintext(blob_str(pIn), pOut); }else{ blob_append(pOut, "\n", 1); blob_append(pOut, blob_buffer(pIn), blob_size(pIn)); } blob_reset(&html); blob_reset(&title); } /* ** Query pQuery is pointing at a single row of output. Append a text ** representation of every text-compatible column to pAccum. */ static void append_all_ticket_fields(Blob *pAccum, Stmt *pQuery, int iTitle){ int n = db_column_count(pQuery); int i; const char *zMime = 0; if( iTitle>=0 && iTitle<n ){ if( db_column_type(pQuery,iTitle)==SQLITE_TEXT ){ blob_append(pAccum, db_column_text(pQuery,iTitle), -1); } blob_append(pAccum, "\n", 1); } for(i=0; i<n; i++){ const char *zColName = db_column_name(pQuery,i); int eType = db_column_type(pQuery,i); if( i==iTitle ) continue; if( fossil_strnicmp(zColName,"tkt_",4)==0 ) continue; if( fossil_strnicmp(zColName,"private_",8)==0 ) continue; if( eType==SQLITE_BLOB || eType==SQLITE_NULL ) continue; if( fossil_stricmp(zColName,"mimetype")==0 ){ zMime = db_column_text(pQuery,i); if( fossil_strcmp(zMime,"text/plain")==0 ) zMime = 0; }else if( zMime==0 || eType!=SQLITE_TEXT ){ blob_appendf(pAccum, "%s: %s |\n", zColName, db_column_text(pQuery,i)); }else{ Blob txt; blob_init(&txt, db_column_text(pQuery,i), -1); blob_appendf(pAccum, "%s: ", zColName); get_stext_by_mimetype(&txt, zMime, pAccum); blob_append(pAccum, " |", 2); blob_reset(&txt); } } } /* ** Return "search text" - a reduced version of a document appropriate for ** full text search and/or for constructing a search result snippet. ** ** cType: d Embedded documentation ** w Wiki page ** c Check-in comment ** t Ticket text ** ** rid The RID of an artifact that defines the object ** being searched. ** ** zName Name of the object being searched. */ void search_stext( char cType, /* Type of document */ int rid, /* BLOB.RID or TAG.TAGID value for document */ const char *zName, /* Auxiliary information */ Blob *pOut /* OUT: Initialize to the search text */ ){ blob_init(pOut, 0, 0); switch( cType ){ case 'd': { /* Documents */ Blob doc; content_get(rid, &doc); blob_to_utf8_no_bom(&doc, 0); get_stext_by_mimetype(&doc, mimetype_from_name(zName), pOut); blob_reset(&doc); break; } case 'w': { /* Wiki */ Manifest *pWiki = manifest_get(rid, CFTYPE_WIKI,0); Blob wiki; if( pWiki==0 ) break; blob_init(&wiki, pWiki->zWiki, -1); get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype), pOut); blob_reset(&wiki); manifest_destroy(pWiki); break; } case 'c': { /* Check-in Comments */ static Stmt q; static int isPlainText = -1; db_static_prepare(&q, "SELECT coalesce(ecomment,comment)" " ||' (user: '||coalesce(euser,user,'?')" " ||', tags: '||" " (SELECT group_concat(substr(tag.tagname,5),',')" " FROM tag, tagxref" " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" " AND tagxref.rid=event.objid AND tagxref.tagtype>0)" " ||')'" " FROM event WHERE objid=:x AND type='ci'"); if( isPlainText<0 ){ isPlainText = db_get_boolean("timeline-plaintext",0); } db_bind_int(&q, ":x", rid); if( db_step(&q)==SQLITE_ROW ){ blob_append(pOut, "\n", 1); if( isPlainText ){ db_column_blob(&q, 0, pOut); }else{ Blob x; blob_init(&x,0,0); db_column_blob(&q, 0, &x); get_stext_by_mimetype(&x, "text/x-fossil-wiki", pOut); blob_reset(&x); } } db_reset(&q); break; } case 't': { /* Tickets */ static Stmt q1; static int iTitle = -1; db_static_prepare(&q1, "SELECT * FROM ticket WHERE tkt_id=:rid"); db_bind_int(&q1, ":rid", rid); if( db_step(&q1)==SQLITE_ROW ){ if( iTitle<0 ){ int n = db_column_count(&q1); for(iTitle=0; iTitle<n; iTitle++){ if( fossil_stricmp(db_column_name(&q1,iTitle),"title")==0 ) break; } } append_all_ticket_fields(pOut, &q1, iTitle); } db_reset(&q1); if( db_table_exists("repository","ticketchng") ){ static Stmt q2; db_static_prepare(&q2, "SELECT * FROM ticketchng WHERE tkt_id=:rid" " ORDER BY tkt_mtime"); db_bind_int(&q2, ":rid", rid); while( db_step(&q2)==SQLITE_ROW ){ append_all_ticket_fields(pOut, &q2, -1); } db_reset(&q2); } break; } } } /* ** This routine is a wrapper around search_stext(). ** ** This routine looks up the search text, stores it in an internal ** buffer, and returns a pointer to the text. Subsequent requests ** for the same document return the same pointer. The returned pointer ** is valid until the next invocation of this routine. Call this routine ** with an eType of 0 to clear the cache. */ char *search_stext_cached( char cType, /* Type of document */ int rid, /* BLOB.RID or TAG.TAGID value for document */ const char *zName, /* Auxiliary information */ int *pnTitle /* OUT: length of title in bytes excluding \n */ ){ static struct { Blob stext; /* Cached search text */ char cType; /* The type */ int rid; /* The RID */ int nTitle; /* Number of bytes in title */ } cache; int i; char *z; if( cType!=cache.cType || rid!=cache.rid ){ if( cache.rid>0 ){ blob_reset(&cache.stext); }else{ blob_init(&cache.stext,0,0); } cache.cType = cType; cache.rid = rid; if( cType==0 ) return 0; search_stext(cType, rid, zName, &cache.stext); z = blob_str(&cache.stext); for(i=0; z[i] && z[i]!='\n'; i++){} cache.nTitle = i; } if( pnTitle ) *pnTitle = cache.nTitle; return blob_str(&cache.stext); } /* ** COMMAND: test-search-stext ** ** Usage: fossil test-search-stext TYPE ARG1 ARG2 */ void test_search_stext(void){ Blob out; db_find_and_open_repository(0,0); if( g.argc!=5 ) usage("TYPE RID NAME"); search_stext(g.argv[2][0], atoi(g.argv[3]), g.argv[4], &out); fossil_print("%s\n",blob_str(&out)); blob_reset(&out); } /* ** COMMAND: test-convert-stext ** ** Usage: fossil test-convert-stext FILE MIMETYPE ** ** Read the content of FILE and convert it to stext according to MIMETYPE. ** Send the result to standard output. */ void test_convert_stext(void){ Blob in, out; db_find_and_open_repository(0,0); if( g.argc!=4 ) usage("FILENAME MIMETYPE"); blob_read_from_file(&in, g.argv[2]); blob_init(&out, 0, 0); get_stext_by_mimetype(&in, g.argv[3], &out); fossil_print("%s\n",blob_str(&out)); blob_reset(&in); blob_reset(&out); } /* The schema for the full-text index */ static const char zFtsSchema[] = @ -- One entry for each possible search result @ CREATE TABLE IF NOT EXISTS "%w".ftsdocs( @ rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.docid @ type CHAR(1), -- Type of document @ rid INTEGER, -- BLOB.RID or TAG.TAGID for the document @ name TEXT, -- Additional document description @ idxed BOOLEAN, -- True if currently in the index @ label TEXT, -- Label to print on search results @ url TEXT, -- URL to access this document @ mtime DATE, -- Date when document created @ bx TEXT, -- Temporary "body" content cache @ UNIQUE(type,rid) @ ); @ CREATE INDEX "%w".ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0; @ CREATE INDEX "%w".ftsdocName ON ftsdocs(name) WHERE type='w'; @ CREATE VIEW IF NOT EXISTS "%w".ftscontent AS @ SELECT rowid, type, rid, name, idxed, label, url, mtime, @ title(type,rid,name) AS 'title', body(type,rid,name) AS 'body' @ FROM ftsdocs; @ CREATE VIRTUAL TABLE IF NOT EXISTS "%w".ftsidx @ USING fts4(content="ftscontent", title, body%s); ; static const char zFtsDrop[] = @ DROP TABLE IF EXISTS "%w".ftsidx; @ DROP VIEW IF EXISTS "%w".ftscontent; @ DROP TABLE IF EXISTS "%w".ftsdocs; ; /* ** Create or drop the tables associated with a full-text index. */ static int searchIdxExists = -1; void search_create_index(void){ const char *zDb = db_name("repository"); int useStemmer = db_get_boolean("search-stemmer",0); const char *zExtra = useStemmer ? ",tokenize=porter" : ""; search_sql_setup(g.db); db_multi_exec(zFtsSchema/*works-like:"%w%w%w%w%w%s"*/, zDb, zDb, zDb, zDb, zDb, zExtra/*safe-for-%s*/); searchIdxExists = 1; } void search_drop_index(void){ const char *zDb = db_name("repository"); db_multi_exec(zFtsDrop/*works-like:"%w%w%w"*/, zDb, zDb, zDb); searchIdxExists = 0; } /* ** Return true if the full-text search index exists */ int search_index_exists(void){ if( searchIdxExists<0 ){ searchIdxExists = db_table_exists("repository","ftsdocs"); } return searchIdxExists; } /* ** Fill the FTSDOCS table with unindexed entries for everything ** in the repository. This uses INSERT OR IGNORE so entries already ** in FTSDOCS are unchanged. */ void search_fill_index(void){ if( !search_index_exists() ) return; search_sql_setup(g.db); db_multi_exec( "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)" " SELECT 'c', objid, 0 FROM event WHERE type='ci';" ); db_multi_exec( "WITH latest_wiki(rid,name,mtime) AS (" " SELECT tagxref.rid, substr(tag.tagname,6), max(tagxref.mtime)" " FROM tag, tagxref" " WHERE tag.tagname GLOB 'wiki-*'" " AND tagxref.tagid=tag.tagid" " AND tagxref.value>0" " GROUP BY 2" ") INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed)" " SELECT 'w', rid, name, 0 FROM latest_wiki;" ); db_multi_exec( "INSERT OR IGNORE INTO ftsdocs(type,rid,idxed)" " SELECT 't', tkt_id, 0 FROM ticket;" ); } /* ** The document described by cType,rid,zName is about to be added or ** updated. If the document has already been indexed, then unindex it ** now while we still have access to the old content. Add the document ** to the queue of documents that need to be indexed or reindexed. */ void search_doc_touch(char cType, int rid, const char *zName){ if( search_index_exists() ){ char zType[2]; zType[0] = cType; zType[1] = 0; search_sql_setup(g.db); db_multi_exec( "DELETE FROM ftsidx WHERE docid IN" " (SELECT rowid FROM ftsdocs WHERE type=%Q AND rid=%d AND idxed)", zType, rid ); db_multi_exec( "REPLACE INTO ftsdocs(type,rid,name,idxed)" " VALUES(%Q,%d,%Q,0)", zType, rid, zName ); if( cType=='w' ){ db_multi_exec( "DELETE FROM ftsidx WHERE docid IN" " (SELECT rowid FROM ftsdocs WHERE type='w' AND name=%Q AND idxed)", zName ); db_multi_exec( "DELETE FROM ftsdocs WHERE type='w' AND name=%Q AND rid!=%d", zName, rid ); } } } /* ** If the doc-glob and doc-br settings are valid for document search ** and if the latest check-in on doc-br is in the unindexed set of ** check-ins, then update all 'd' entries in FTSDOCS that have ** changed. */ static void search_update_doc_index(void){ const char *zDocBr = db_get("doc-branch","trunk"); int ckid = zDocBr ? symbolic_name_to_rid(zDocBr,"ci") : 0; double rTime; char *zBrUuid; if( ckid==0 ) return; if( !db_exists("SELECT 1 FROM ftsdocs WHERE type='c' AND rid=%d" " AND NOT idxed", ckid) ) return; /* If we get this far, it means that changes to 'd' entries are ** required. */ rTime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", ckid); zBrUuid = db_text("","SELECT substr(uuid,1,20) FROM blob WHERE rid=%d",ckid); db_multi_exec( "CREATE TEMP TABLE current_docs(rid INTEGER PRIMARY KEY, name);" "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" "INSERT OR IGNORE INTO current_docs(rid, name)" " SELECT blob.rid, foci.filename FROM foci, blob" " WHERE foci.checkinID=%d AND blob.uuid=foci.uuid" " AND %z", ckid, glob_expr("foci.filename", db_get("doc-glob","")) ); db_multi_exec( "DELETE FROM ftsidx WHERE docid IN" " (SELECT rowid FROM ftsdocs WHERE type='d'" " AND rid NOT IN (SELECT rid FROM current_docs))" ); db_multi_exec( "DELETE FROM ftsdocs WHERE type='d'" " AND rid NOT IN (SELECT rid FROM current_docs)" ); db_multi_exec( "INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,bx,url,mtime)" " SELECT 'd', rid, name, 0," " title('d',rid,name)," " body('d',rid,name)," " printf('/doc/%q/%%s',urlencode(name))," " %.17g" " FROM current_docs", zBrUuid, rTime ); db_multi_exec( "INSERT INTO ftsidx(docid,title,body)" " SELECT rowid, label, bx FROM ftsdocs WHERE type='d' AND NOT idxed" ); db_multi_exec( "UPDATE ftsdocs SET" " idxed=1," " bx=NULL," " label='Document: '||label" " WHERE type='d' AND NOT idxed" ); } /* ** Deal with all of the unindexed 'c' terms in FTSDOCS */ static void search_update_checkin_index(void){ db_multi_exec( "INSERT INTO ftsidx(docid,title,body)" " SELECT rowid, '', body('c',rid,NULL) FROM ftsdocs" " WHERE type='c' AND NOT idxed;" ); db_multi_exec( "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)" " SELECT ftsdocs.rowid, 1, 'c', ftsdocs.rid, NULL," " printf('Check-in [%%.16s] on %%s',blob.uuid,datetime(event.mtime))," " printf('/timeline?y=ci&c=%%.20s',blob.uuid)," " event.mtime" " FROM ftsdocs, event, blob" " WHERE ftsdocs.type='c' AND NOT ftsdocs.idxed" " AND event.objid=ftsdocs.rid" " AND blob.rid=ftsdocs.rid" ); } /* ** Deal with all of the unindexed 't' terms in FTSDOCS */ static void search_update_ticket_index(void){ db_multi_exec( "INSERT INTO ftsidx(docid,title,body)" " SELECT rowid, title('t',rid,NULL), body('t',rid,NULL) FROM ftsdocs" " WHERE type='t' AND NOT idxed;" ); if( db_changes()==0 ) return; db_multi_exec( "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)" " SELECT ftsdocs.rowid, 1, 't', ftsdocs.rid, NULL," " printf('Ticket: %%s (%%s)',title('t',tkt_id,null)," " datetime(tkt_mtime))," " printf('/tktview/%%.20s',tkt_uuid)," " tkt_mtime" " FROM ftsdocs, ticket" " WHERE ftsdocs.type='t' AND NOT ftsdocs.idxed" " AND ticket.tkt_id=ftsdocs.rid" ); } /* ** Deal with all of the unindexed 'w' terms in FTSDOCS */ static void search_update_wiki_index(void){ db_multi_exec( "INSERT INTO ftsidx(docid,title,body)" " SELECT rowid, title('w',rid,NULL),body('w',rid,NULL) FROM ftsdocs" " WHERE type='w' AND NOT idxed;" ); if( db_changes()==0 ) return; db_multi_exec( "REPLACE INTO ftsdocs(rowid,idxed,type,rid,name,label,url,mtime)" " SELECT ftsdocs.rowid, 1, 'w', ftsdocs.rid, ftsdocs.name," " 'Wiki: '||ftsdocs.name," " '/wiki?name='||urlencode(ftsdocs.name)," " tagxref.mtime" " FROM ftsdocs, tagxref" " WHERE ftsdocs.type='w' AND NOT ftsdocs.idxed" " AND tagxref.rid=ftsdocs.rid" ); } /* ** Deal with all of the unindexed entries in the FTSDOCS table - that ** is to say, all the entries with FTSDOCS.IDXED=0. Add them to the ** index. */ void search_update_index(unsigned int srchFlags){ if( !search_index_exists() ) return; if( !db_exists("SELECT 1 FROM ftsdocs WHERE NOT idxed") ) return; search_sql_setup(g.db); if( srchFlags & (SRCH_CKIN|SRCH_DOC) ){ search_update_doc_index(); search_update_checkin_index(); } if( srchFlags & SRCH_TKT ){ search_update_ticket_index(); } if( srchFlags & SRCH_WIKI ){ search_update_wiki_index(); } } /* ** Construct, prepopulate, and then update the full-text index. */ void search_rebuild_index(void){ fossil_print("rebuilding the search index..."); fflush(stdout); search_create_index(); search_fill_index(); search_update_index(search_restrict(SRCH_ALL)); fossil_print(" done\n"); } /* ** COMMAND: fts-config* ** ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT? ** ** The "fossil fts-config" command configures the full-text search capabilities ** of the repository. Subcommands: ** ** reindex Rebuild the search index. This is a no-op if ** index search is disabled ** ** index (on|off) Turn the search index on or off ** ** enable cdtw Enable various kinds of search. c=Check-ins, ** d=Documents, t=Tickets, w=Wiki. ** ** disable cdtw Disable versious kinds of search ** ** stemmer (on|off) Turn the Porter stemmer on or off for indexed ** search. (Unindexed search is never stemmed.) ** ** The current search settings are displayed after any changes are applied. ** Run this command with no arguments to simply see the settings. */ void test_fts_cmd(void){ static const struct { int iCmd; const char *z; } aCmd[] = { { 1, "reindex" }, { 2, "index" }, { 3, "disable" }, { 4, "enable" }, { 5, "stemmer" }, }; static const struct { char *zSetting; char *zName; char *zSw; } aSetng[] = { { "search-ckin", "check-in search:", "c" }, { "search-doc", "document search:", "d" }, { "search-tkt", "ticket search:", "t" }, { "search-wiki", "wiki search:", "w" }, }; char *zSubCmd = 0; int i, j, n; int iCmd = 0; int iAction = 0; db_find_and_open_repository(0, 0); if( g.argc>2 ){ zSubCmd = g.argv[2]; n = (int)strlen(zSubCmd); for(i=0; i<ArraySize(aCmd); i++){ if( fossil_strncmp(aCmd[i].z, zSubCmd, n)==0 ) break; } if( i>=ArraySize(aCmd) ){ Blob all; blob_init(&all,0,0); for(i=0; i<ArraySize(aCmd); i++) blob_appendf(&all, " %s", aCmd[i].z); fossil_fatal("unknown \"%s\" - should be on of:%s", zSubCmd, blob_str(&all)); return; } iCmd = aCmd[i].iCmd; } if( iCmd==1 ){ if( search_index_exists() ) iAction = 2; } if( iCmd==2 ){ if( g.argc<3 ) usage("index (on|off)"); iAction = 1 + is_truth(g.argv[3]); } db_begin_transaction(); /* Adjust search settings */ if( iCmd==3 || iCmd==4 ){ const char *zCtrl; if( g.argc<4 ) usage(mprintf("%s STRING",zSubCmd)); zCtrl = g.argv[3]; for(j=0; j<ArraySize(aSetng); j++){ if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){ db_set_int(aSetng[j].zSetting, iCmd-3, 0); } } } if( iCmd==5 ){ if( g.argc<4 ) usage("porter ON/OFF"); db_set_int("search-stemmer", is_truth(g.argv[3]), 0); } /* destroy or rebuild the index, if requested */ if( iAction>=1 ){ search_drop_index(); } if( iAction>=2 ){ search_rebuild_index(); } /* Always show the status before ending */ for(i=0; i<ArraySize(aSetng); i++){ fossil_print("%-16s %s\n", aSetng[i].zName, db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off"); } fossil_print("%-16s %s\n", "Porter stemmer:", db_get_boolean("search-stemmer",0) ? "on" : "off"); if( search_index_exists() ){ fossil_print("%-16s enabled\n", "full-text index:"); fossil_print("%-16s %d\n", "documents:", db_int(0, "SELECT count(*) FROM ftsdocs")); }else{ fossil_print("%-16s disabled\n", "full-text index:"); } db_end_transaction(0); } |
Changes to src/setup.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Implementation of the Setup page */ #include <assert.h> | > | | > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Implementation of the Setup page */ #include "config.h" #include <assert.h> #include "setup.h" #if INTERFACE #define ArraySize(x) (sizeof(x)/sizeof(x[0])) #endif /* ** The table of web pages supported by this application is generated ** automatically by the "mkindex" program and written into a file ** named "page_index.h". We include that file here to get access ** to the table. */ |
︙ | ︙ | |||
44 45 46 47 48 49 50 51 52 53 54 55 56 57 | if( zLink && zLink[0] ){ @ <a href="%s(zLink)">%h(zTitle)</a> }else{ @ %h(zTitle) } @ </td><td width="5"></td><td valign="top">%h(zDesc)</td></tr> } /* ** WEBPAGE: /setup */ void setup_page(void){ login_check_credentials(); if( !g.perm.Setup ){ | > > | | | > > > > > > > > > > > > > > | > > | < < < < < < | | > > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | if( zLink && zLink[0] ){ @ <a href="%s(zLink)">%h(zTitle)</a> }else{ @ %h(zTitle) } @ </td><td width="5"></td><td valign="top">%h(zDesc)</td></tr> } /* ** WEBPAGE: /setup */ void setup_page(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); } style_header("Server Administration"); /* Make sure the header contains <base href="...">. Issue a warning ** if it does not. */ if( !cgi_header_contains("<base href=") ){ @ <p class="generalError"><b>Configuration Error:</b> Please add @ <tt><base href="$secureurl/$current_page"></tt> after @ <tt><head></tt> in the <a href="setup_skinedit?w=2">HTML header</a>!</p> } #if !defined(_WIN32) /* Check for /dev/null and /dev/urandom. We want both devices to be present, ** but they are sometimes omitted (by mistake) from chroot jails. */ if( access("/dev/null", R_OK|W_OK) ){ @ <p class="generalError">WARNING: Device "/dev/null" is not available @ for reading and writing.</p> } if( access("/dev/urandom", R_OK) ){ @ <p class="generalError">WARNING: Device "/dev/urandom" is not available @ for reading. This means that the pseudo-random number generator used @ by SQLite will be poorly seeded.</p> } #endif @ <table border="0" cellspacing="3"> setup_menu_entry("Users", "setup_ulist", "Grant privileges to individual users."); setup_menu_entry("Access", "setup_access", "Control access settings."); setup_menu_entry("Configuration", "setup_config", "Configure the WWW components of the repository"); setup_menu_entry("Settings", "setup_settings", "Web interface to the \"fossil settings\" command"); setup_menu_entry("Timeline", "setup_timeline", "Timeline display preferences"); setup_menu_entry("Login-Group", "setup_login_group", "Manage single sign-on between this repository and others" " on the same server"); setup_menu_entry("Tickets", "tktsetup", "Configure the trouble-ticketing system for this repository"); setup_menu_entry("Search","srchsetup", "Configure the built-in search engine"); setup_menu_entry("Transfers", "xfersetup", "Configure the transfer system for this repository"); setup_menu_entry("Skins", "setup_skin", "Select and/or modify the web interface \"skins\""); setup_menu_entry("Moderation", "setup_modreq", "Enable/Disable requiring moderator approval of Wiki and/or Ticket" " changes and attachments."); setup_menu_entry("Ad-Unit", "setup_adunit", "Edit HTML text for an ad unit inserted after the menu bar"); setup_menu_entry("Logo", "setup_logo", "Change the logo and background images for the server"); setup_menu_entry("Shunned", "shun", "Show artifacts that are shunned by this repository"); setup_menu_entry("Artifact Receipts Log", "rcvfromlist", "A record of received artifacts and their sources"); setup_menu_entry("User Log", "access_log", "A record of login attempts"); setup_menu_entry("Administrative Log", "admin_log", "View the admin_log entries"); setup_menu_entry("Stats", "stat", "Display repository statistics"); setup_menu_entry("SQL", "admin_sql", "Enter raw SQL commands"); setup_menu_entry("TH1", "admin_th1", "Enter raw TH1 commands"); @ </table> |
︙ | ︙ | |||
126 127 128 129 130 131 132 | */ void setup_ulist(void){ Stmt s; int prevLevel = 0; login_check_credentials(); if( !g.perm.Admin ){ | | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | */ void setup_ulist(void){ Stmt s; int prevLevel = 0; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_submenu_element("Add", "Add User", "setup_uedit"); style_header("User List"); @ <table class="usetupLayoutTable"> @ <tr><td class="usetupColumnLayout"> @ <span class="note">Users:</span> @ <table class="usetupUserList"> prevLevel = 0; db_prepare(&s, "SELECT uid, login, cap, info, 1 FROM user" " WHERE login IN ('anonymous','nobody','developer','reader') " " UNION ALL " "SELECT uid, login, cap, info, 2 FROM user" " WHERE login NOT IN ('anonymous','nobody','developer','reader') " "ORDER BY 5, 2 COLLATE nocase" ); while( db_step(&s)==SQLITE_ROW ){ int iLevel = db_column_int(&s, 4); const char *zCap = db_column_text(&s, 2); const char *zLogin = db_column_text(&s, 1); if( iLevel>prevLevel ){ if( prevLevel>0 ){ |
︙ | ︙ | |||
247 248 249 250 251 252 253 | @ <td><i>Developer:</i> Inherit privileges of @ user <tt>developer</tt></td></tr> @ <tr><th valign="top">w</th> @ <td><i>Write-Tkt:</i> Edit tickets</td></tr> @ <tr><th valign="top">x</th> @ <td><i>Private:</i> Push and/or pull private branches</td></tr> @ <tr><th valign="top">z</th> | | < < < | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | @ <td><i>Developer:</i> Inherit privileges of @ user <tt>developer</tt></td></tr> @ <tr><th valign="top">w</th> @ <td><i>Write-Tkt:</i> Edit tickets</td></tr> @ <tr><th valign="top">x</th> @ <td><i>Private:</i> Push and/or pull private branches</td></tr> @ <tr><th valign="top">z</th> @ <td><i>Zip download:</i> Download a ZIP archive or tarball</td></tr> @ </table> @ </li> @ @ <li><p> @ Every user, logged in or not, inherits the privileges of @ <span class="usertype">nobody</span>. @ </p></li> |
︙ | ︙ | |||
278 279 280 281 282 283 284 285 286 287 288 289 290 291 | @ <span class="usertype">anonymous</span>, and @ <span class="usertype">nobody</span>. @ </p></li> @ @ </ol> @ </td></tr></table> style_footer(); } /* ** Return true if zPw is a valid password string. A valid ** password string is: ** ** (1) A zero-length string, or | > | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | @ <span class="usertype">anonymous</span>, and @ <span class="usertype">nobody</span>. @ </p></li> @ @ </ol> @ </td></tr></table> style_footer(); db_finalize(&s); } /* ** Return true if zPw is a valid password string. A valid ** password string is: ** ** (1) A zero-length string, or |
︙ | ︙ | |||
307 308 309 310 311 312 313 | const char *zOldLogin; int doWrite; int uid, i; int higherUser = 0; /* True if user being edited is SETUP and the */ /* user doing the editing is ADMIN. Disallow editing */ char *inherit[128]; int a[128]; | | | | | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | const char *zOldLogin; int doWrite; int uid, i; int higherUser = 0; /* True if user being edited is SETUP and the */ /* user doing the editing is ADMIN. Disallow editing */ char *inherit[128]; int a[128]; const char *oa[128]; /* Must have ADMIN privileges to access this page */ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } /* Check to see if an ADMIN user is trying to edit a SETUP account. ** Don't allow that. */ zId = PD("id", "0"); uid = atoi(zId); if( zId && !g.perm.Setup && uid>0 ){ char *zOldCaps; zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid); higherUser = zOldCaps && strchr(zOldCaps,'s'); } if( P("can") ){ cgi_redirect("setup_ulist"); /* User pressed the Cancel button */ return; } /* If we have all the necessary information, write the new or ** modified user record. After writing the user record, redirect ** to the page that displays a list of users. */ |
︙ | ︙ | |||
363 364 365 366 367 368 369 | } if( isValidPwString(zPw) ){ zPw = sha1_shared_secret(zPw, zLogin, 0); }else{ zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid); } zOldLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid); | < | < | | > > | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | } if( isValidPwString(zPw) ){ zPw = sha1_shared_secret(zPw, zLogin, 0); }else{ zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid); } zOldLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid); if( db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid) ){ style_header("User Creation Error"); @ <span class="loginError">Login "%h(zLogin)" is already used by @ a different user.</span> @ @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p> style_footer(); return; } login_verify_csrf_secret(); db_multi_exec( "REPLACE INTO user(uid,login,info,pw,cap,mtime) " "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", uid, zLogin, P("info"), zPw, zCap ); admin_log( "Updated user [%q] with capabilities [%q].", zLogin, zCap ); if( atoi(PD("all","0"))>0 ){ Blob sql; char *zErr = 0; blob_zero(&sql); if( zOldLogin==0 ){ blob_appendf(&sql, "INSERT INTO user(login)" |
︙ | ︙ | |||
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | " mtime=now()" " WHERE login=%Q;", zLogin, P("pw"), zLogin, P("info"), zCap, zOldLogin ); login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); blob_reset(&sql); if( zErr ){ style_header("User Change Error"); @ <span class="loginError">%s(zErr)</span> @ @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p> style_footer(); return; } } | > > > > | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | " mtime=now()" " WHERE login=%Q;", zLogin, P("pw"), zLogin, P("info"), zCap, zOldLogin ); login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); blob_reset(&sql); admin_log( "Updated user [%q] in all login groups " "with capabilities [%q].", zLogin, zCap ); if( zErr ){ style_header("User Change Error"); admin_log( "Error updating user '%q': %s'.", zLogin, zErr ); @ <span class="loginError">%s(zErr)</span> @ @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p> style_footer(); return; } } |
︙ | ︙ | |||
443 444 445 446 447 448 449 | /* figure out inherited permissions */ memset(inherit, 0, sizeof(inherit)); if( fossil_strcmp(zLogin, "developer") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > | | > > > > > > > > | | | | | | | | | > > | | | | | | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 | /* figure out inherited permissions */ memset(inherit, 0, sizeof(inherit)); if( fossil_strcmp(zLogin, "developer") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritDeveloper\"><sub>[D]</sub></span>"; } free(z2); } if( fossil_strcmp(zLogin, "reader") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritReader\"><sub>[R]</sub></span>"; } free(z2); } if( fossil_strcmp(zLogin, "anonymous") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritAnonymous\"><sub>[A]</sub></span>"; } free(z2); } if( fossil_strcmp(zLogin, "nobody") ){ char *z1, *z2; z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'"); while( z1 && *z1 ){ inherit[0x7f & *(z1++)] = "<span class=\"ueditInheritNobody\"><sub>[N]</sub></span>"; } free(z2); } /* Begin generating the page */ style_submenu_element("Cancel", "Cancel", "setup_ulist"); if( uid ){ style_header("Edit User %h", zLogin); }else{ style_header("Add A New User"); } @ <div class="ueditCapBox"> @ <form action="%s(g.zPath)" method="post"><div> login_insert_csrf_secret(); if( login_is_special(zLogin) ){ @ <input type="hidden" name="login" value="%s(zLogin)"> @ <input type="hidden" name="info" value=""> @ <input type="hidden" name="pw" value="*"> } @ <script type='text/javascript'> @ function updateCapabilityString(){ @ /* @ ** This function updates the "#usetupEditCapability" span content @ ** with the capabilities selected by the interactive user, based @ ** upon the state of the capability checkboxes. @ */ @ try { @ var inputs = document.getElementsByTagName('input'); @ if( inputs && inputs.length ){ @ var output = document.getElementById('usetupEditCapability'); @ if( output ){ @ var permsIds = [], x = 0; @ for(var i = 0; i < inputs.length; i++){ @ var e = inputs[i]; @ if( !e.name || !e.type ) continue; @ if( e.type.toLowerCase()!=='checkbox' ) continue; @ if( e.name.length===2 && e.name[0]==='a' ){ @ // looks like a capability checkbox @ if( e.checked ){ @ // grab the second character of the element @ // name, which is the textual flag for this @ // capability, and then add it to the result @ // array. @ permsIds[x++] = e.name[1]; @ } @ } @ } @ permsIds.sort(); @ output.innerHTML = permsIds.join(''); @ } @ } @ } catch (e) { @ /* ignore errors */ @ } @ } @ </script> @ <table> @ <tr> @ <td class="usetupEditLabel">User ID:</td> if( uid ){ @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" /></td> }else{ @ <td>(new user)<input type="hidden" name="id" value="0" /></td> } @ </tr> @ <tr> @ <td class="usetupEditLabel">Login:</td> if( login_is_special(zLogin) ){ @ <td><b>%h(zLogin)</b></td> }else{ @ <td><input type="text" name="login" value="%h(zLogin)" /></td> @ </tr> @ <tr> @ <td class="usetupEditLabel">Contact Info:</td> @ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td> } @ </tr> @ <tr> @ <td class="usetupEditLabel">Capabilities:</td> @ <td> #define B(x) inherit[x] @ <table border=0><tr><td valign="top"> if( g.perm.Setup ){ @ <label><input type="checkbox" name="as"%s(oa['s']) @ onchange="updateCapabilityString()"/> @ Setup%s(B('s'))</label><br /> } @ <label><input type="checkbox" name="aa"%s(oa['a']) @ onchange="updateCapabilityString()" /> @ Admin%s(B('a'))</label><br /> @ <label><input type="checkbox" name="ad"%s(oa['d']) @ onchange="updateCapabilityString()" /> @ Delete%s(B('d'))</label><br /> @ <label><input type="checkbox" name="ae"%s(oa['e']) @ onchange="updateCapabilityString()" /> @ Email%s(B('e'))</label><br /> @ <label><input type="checkbox" name="ap"%s(oa['p']) @ onchange="updateCapabilityString()" /> @ Password%s(B('p'))</label><br /> @ <label><input type="checkbox" name="ai"%s(oa['i']) @ onchange="updateCapabilityString()" /> @ Check-In%s(B('i'))</label><br /> @ <label><input type="checkbox" name="ao"%s(oa['o']) @ onchange="updateCapabilityString()" /> @ Check-Out%s(B('o'))</label><br /> @ <label><input type="checkbox" name="ah"%s(oa['h']) @ onchange="updateCapabilityString()" /> @ Hyperlinks%s(B('h'))</label><br /> @ <label><input type="checkbox" name="ab"%s(oa['b']) @ onchange="updateCapabilityString()" /> @ Attachments%s(B('b'))</label><br /> @ </td><td><td width="40"></td><td valign="top"> @ <label><input type="checkbox" name="au"%s(oa['u']) @ onchange="updateCapabilityString()" /> @ Reader%s(B('u'))</label><br /> @ <label><input type="checkbox" name="av"%s(oa['v']) @ onchange="updateCapabilityString()" /> @ Developer%s(B('v'))</label><br /> @ <label><input type="checkbox" name="ag"%s(oa['g']) @ onchange="updateCapabilityString()" /> @ Clone%s(B('g'))</label><br /> @ <label><input type="checkbox" name="aj"%s(oa['j']) @ onchange="updateCapabilityString()" /> @ Read Wiki%s(B('j'))</label><br /> @ <label><input type="checkbox" name="af"%s(oa['f']) @ onchange="updateCapabilityString()" /> @ New Wiki%s(B('f'))</label><br /> @ <label><input type="checkbox" name="am"%s(oa['m']) @ onchange="updateCapabilityString()" /> @ Append Wiki%s(B('m'))</label><br /> @ <label><input type="checkbox" name="ak"%s(oa['k']) @ onchange="updateCapabilityString()" /> @ Write Wiki%s(B('k'))</label><br /> @ <label><input type="checkbox" name="al"%s(oa['l']) @ onchange="updateCapabilityString()" /> @ Moderate Wiki%s(B('l'))</label><br /> @ </td><td><td width="40"></td><td valign="top"> @ <label><input type="checkbox" name="ar"%s(oa['r']) @ onchange="updateCapabilityString()" /> @ Read Ticket%s(B('r'))</label><br /> @ <label><input type="checkbox" name="an"%s(oa['n']) @ onchange="updateCapabilityString()" /> @ New Tickets%s(B('n'))</label><br /> @ <label><input type="checkbox" name="ac"%s(oa['c']) @ onchange="updateCapabilityString()" /> @ Append To Ticket%s(B('c'))</label><br /> @ <label><input type="checkbox" name="aw"%s(oa['w']) @ onchange="updateCapabilityString()" /> @ Write Tickets%s(B('w'))</label><br /> @ <label><input type="checkbox" name="aq"%s(oa['q']) @ onchange="updateCapabilityString()" /> @ Moderate Tickets%s(B('q'))</label><br /> @ <label><input type="checkbox" name="at"%s(oa['t']) @ onchange="updateCapabilityString()" /> @ Ticket Report%s(B('t'))</label><br /> @ <label><input type="checkbox" name="ax"%s(oa['x']) @ onchange="updateCapabilityString()" /> @ Private%s(B('x'))</label><br /> @ <label><input type="checkbox" name="az"%s(oa['z']) @ onchange="updateCapabilityString()" /> @ Download Zip%s(B('z'))</label> @ </td></tr> @ </table> @ </td> @ </tr> @ <tr> @ <td class="usetupEditLabel">Selected Cap.:</td> @ <td> @ <span id="usetupEditCapability">(missing JS?)</span> @ </td> @ </tr> if( !login_is_special(zLogin) ){ @ <tr> @ <td align="right">Password:</td> if( zPw[0] ){ /* Obscure the password for all users */ @ <td><input type="password" name="pw" value="**********" /></td> }else{ /* Show an empty password as an empty input field */ @ <td><input type="password" name="pw" value="" /></td> } @ </tr> } zGroup = login_group_name(); if( zGroup ){ @ <tr> @ <td valign="top" align="right">Scope:</td> @ <td valign="top"> @ <input type="radio" name="all" checked value="0"> @ Apply changes to this repository only.<br /> @ <input type="radio" name="all" value="1"> @ Apply changes to all repositories in the "<b>%h(zGroup)</b>" @ login group.</td></tr> } if( !higherUser ){ @ <tr> @ <td> </td> @ <td><input type="submit" name="submit" value="Apply Changes" /></td> @ </tr> } @ </table> @ </div></form> @ </div> @ <script type='text/javascript'>updateCapabilityString();</script> @ <h2>Privileges And Capabilities:</h2> @ <ul> if( higherUser ){ @ <li><p class="missingPriv"> @ User %h(zLogin) has Setup privileges and you only have Admin privileges @ so you are not permitted to make changes to %h(zLogin). @ </p></li> @ } @ <li><p> @ The <span class="capability">Setup</span> user can make arbitrary @ configuration changes. An <span class="usertype">Admin</span> user @ can add other users and change user privileges @ and reset user passwords. Both automatically get all other privileges @ listed below. Use these two settings with discretion. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritNobody"><sub>N</sub></span>" subscript suffix @ indicates the privileges of <span class="usertype">nobody</span> that @ are available to all users regardless of whether or not they are logged in. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritAnonymous"><sub>A</sub></span>" subscript suffix @ indicates the privileges of <span class="usertype">anonymous</span> that @ are inherited by all logged-in users. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>" subscript suffix @ indicates the privileges of <span class="usertype">developer</span> that @ are inherited by all users with the @ <span class="capability">Developer</span> privilege. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritReader"><sub>R</sub></span>" subscript suffix @ indicates the privileges of <span class="usertype">reader</span> that @ are inherited by all users with the <span class="capability">Reader</span> @ privilege. @ </p></li> @ @ <li><p> @ The <span class="capability">Delete</span> privilege give the user the |
︙ | ︙ | |||
722 723 724 725 726 727 728 | @ <h2>Special Logins</h2> @ @ <ul> @ <li><p> @ No login is required for user <span class="usertype">nobody</span>. The @ capabilities of the <span class="usertype">nobody</span> user are @ inherited by all users, regardless of whether or not they are logged in. | | < | < < < | 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 | @ <h2>Special Logins</h2> @ @ <ul> @ <li><p> @ No login is required for user <span class="usertype">nobody</span>. The @ capabilities of the <span class="usertype">nobody</span> user are @ inherited by all users, regardless of whether or not they are logged in. @ To disable universal access to the repository, make sure that the @ <span class="usertype">nobody</span> user has no capabilities @ enabled. The password for <span class="usertype">nobody</span> is ignored. @ </p></li> @ @ <li><p> @ Login is required for user <span class="usertype">anonymous</span> but the @ password is displayed on the login screen beside the password entry box @ so anybody who can read should be able to login as anonymous. @ On the other hand, spiders and web-crawlers will typically not |
︙ | ︙ | |||
779 780 781 782 783 784 785 786 787 788 789 790 791 792 | zQ = "off"; } if( zQ ){ int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ); if( iQ!=iVal ){ login_verify_csrf_secret(); db_set(zVar, iQ ? "1" : "0", 0); iVal = iQ; } } @ <input type="checkbox" name="%s(zQParm)" if( iVal ){ @ checked="checked" } | > > | 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 | zQ = "off"; } if( zQ ){ int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ); if( iQ!=iVal ){ login_verify_csrf_secret(); db_set(zVar, iQ ? "1" : "0", 0); admin_log("Set option [%q] to [%q].", zVar, iQ ? "on" : "off"); iVal = iQ; } } @ <input type="checkbox" name="%s(zQParm)" if( iVal ){ @ checked="checked" } |
︙ | ︙ | |||
806 807 808 809 810 811 812 813 814 815 816 | const char *zQParm, /* The query parameter */ char *zDflt, /* Default value if VAR table entry does not exist */ int disabled /* 1 if disabled */ ){ const char *zVal = db_get(zVar, zDflt); const char *zQ = P(zQParm); if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ login_verify_csrf_secret(); db_set(zVar, zQ, 0); zVal = zQ; } | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | | | | | | | 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 | const char *zQParm, /* The query parameter */ char *zDflt, /* Default value if VAR table entry does not exist */ int disabled /* 1 if disabled */ ){ const char *zVal = db_get(zVar, zDflt); const char *zQ = P(zQParm); if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ const int nZQ = (int)strlen(zQ); login_verify_csrf_secret(); db_set(zVar, zQ, 0); admin_log("Set entry_attribute %Q to: %.*s%s", zVar, 20, zQ, (nZQ>20 ? "..." : "")); zVal = zQ; } @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" if( disabled ){ @ disabled="disabled" } @ /> <b>%s(zLabel)</b> } /* ** Generate a text box for an attribute. */ const char *textarea_attribute( const char *zLabel, /* The text label on the textarea */ int rows, /* Rows in the textarea */ int cols, /* Columns in the textarea */ const char *zVar, /* The corresponding row in the VAR table */ const char *zQP, /* The query parameter */ const char *zDflt, /* Default value if VAR table entry does not exist */ int disabled /* 1 if the textarea should not be editable */ ){ const char *z = db_get(zVar, (char*)zDflt); const char *zQ = P(zQP); if( zQ && !disabled && fossil_strcmp(zQ,z)!=0){ const int nZQ = (int)strlen(zQ); login_verify_csrf_secret(); db_set(zVar, zQ, 0); admin_log("Set textarea_attribute %Q to: %.*s%s", zVar, 20, zQ, (nZQ>20 ? "..." : "")); z = zQ; } if( rows>0 && cols>0 ){ @ <textarea id="id%s(zQP)" name="%s(zQP)" rows="%d(rows)" if( disabled ){ @ disabled="disabled" } @ cols="%d(cols)">%h(z)</textarea> if( zLabel && *zLabel ){ @ <span class="textareaLabel">%s(zLabel)</span> } } return z; } /* ** Generate a text box for an attribute. */ static void multiple_choice_attribute( const char *zLabel, /* The text label on the menu */ const char *zVar, /* The corresponding row in the VAR table */ const char *zQP, /* The query parameter */ const char *zDflt, /* Default value if VAR table entry does not exist */ int nChoice, /* Number of choices */ const char *const *azChoice /* Choices. 2 per choice: (VAR value, Display) */ ){ const char *z = db_get(zVar, (char*)zDflt); const char *zQ = P(zQP); int i; if( zQ && fossil_strcmp(zQ,z)!=0){ const int nZQ = (int)strlen(zQ); login_verify_csrf_secret(); db_set(zVar, zQ, 0); admin_log("Set multiple_choice_attribute %Q to: %.*s%s", zVar, 20, zQ, (nZQ>20 ? "..." : "")); z = zQ; } @ <select size="1" name="%s(zQP)" id="id%s(zQP)"> for(i=0; i<nChoice*2; i+=2){ const char *zSel = fossil_strcmp(azChoice[i],z)==0 ? " selected" : ""; @ <option value="%h(azChoice[i])"%s(zSel)>%h(azChoice[i+1])</option> } @ </select> <b>%h(zLabel)</b> } /* ** WEBPAGE: setup_access */ void setup_access(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_header("Access Control Settings"); db_begin_transaction(); @ <form action="%s(g.zTop)/setup_access" method="post"><div> login_insert_csrf_secret(); @ <hr /> onoff_attribute("Redirect to HTTPS on the Login page", "redirect-to-https", "redirhttps", 0, 0); @ <p>When selected, force the use of HTTPS for the Login page. @ <p>Details: When enabled, this option causes the $secureurl TH1 @ variable is set to an "https:" variant of $baseurl. Otherwise, @ $secureurl is just an alias for $baseurl. Also when enabled, the @ Login page redirects to https if accessed via http. @ <hr /> onoff_attribute("Require password for local access", "localauth", "localauth", 0, 0); @ <p>When enabled, the password sign-in is always required for @ web access. When disabled, unrestricted web access from 127.0.0.1 @ is allowed for the <a href="%R/help/ui">fossil ui</a> command or @ from the <a href="%R/help/server">fossil server</a>, @ <a href="%R/help/http">fossil http</a> commands when the @ "--localauth" command line options is used, or from the @ <a href="%R/help/cgi">fossil cgi</a> if a line containing @ the word "localauth" appears in the CGI script. @ @ <p>A password is always required if any one or more @ of the following are true: @ <ol> @ <li> This button is checked @ <li> The inbound TCP/IP connection is not from 127.0.0.1 @ <li> The server is started using either of the @ <a href="%R/help/server">fossil server</a> or @ <a href="%R/help/server">fossil http</a> commands @ without the "--localauth" option. @ <li> The server is started from CGI without the "localauth" keyword @ in the CGI script. @ </ol> @ @ <hr /> onoff_attribute("Enable /test_env", |
︙ | ︙ | |||
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 | "30", 0); @ <p>Fossil tries to spend less than this many seconds gathering @ the out-bound data of sync, clone, and pull packets. @ If the client request takes longer, a partial reply is given similar @ to the download packet limit. 30s is a reasonable default.</p> @ <hr /> onoff_attribute( "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", "auto-hyperlink", "autohyperlink", 1, 0); @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users @ including user "nobody", as long as (1) the User-Agent string in the @ HTTP header indicates that the request is coming from an actual human @ being and not a a robot or spider and (2) the user agent is able to @ run Javascript in order to set the href= attribute of hyperlinks. Bots @ and spiders can forge a User-Agent string that makes them seem to be a | > > > > > > > > > > > | | > | > > > > | 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 | "30", 0); @ <p>Fossil tries to spend less than this many seconds gathering @ the out-bound data of sync, clone, and pull packets. @ If the client request takes longer, a partial reply is given similar @ to the download packet limit. 30s is a reasonable default.</p> @ <hr /> entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg", "0.0", 0); @ <p>Some expensive operations (such as computing tarballs, zip archives, @ or annotation/blame pages) are prohibited if the load average on the host @ computer is too large. Set the threshold for disallowing expensive @ computations here. Set this to 0.0 to disable the load average limit. @ This limit is only enforced on Unix servers. On Linux systems, @ access to the /proc virtual filesystem is required, which means this limit @ might not work inside a chroot() jail.</p> @ <hr /> onoff_attribute( "Enable hyperlinks for \"nobody\" based on User-Agent and Javascript", "auto-hyperlink", "autohyperlink", 1, 0); @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users @ including user "nobody", as long as (1) the User-Agent string in the @ HTTP header indicates that the request is coming from an actual human @ being and not a a robot or spider and (2) the user agent is able to @ run Javascript in order to set the href= attribute of hyperlinks. Bots @ and spiders can forge a User-Agent string that makes them seem to be a @ normal browser and they can run javascript just like browsers. But most @ bots do not go to that much trouble so this is normally an effective @ defense.</p> @ @ <p>You do not normally want a bot to walk your entire repository because @ if it does, your server will end up computing diffs and annotations for @ every historical version of every file and creating ZIPs and tarballs of @ every historical check-in, which can use a lot of CPU and bandwidth @ even for relatively small projects.</p> @ @ <p>Additional parameters that control this behavior:</p> @ <blockquote> onoff_attribute("Enable hyperlinks for humans (as deduced from the UserAgent " " HTTP header string)", "auto-hyperlink-ishuman", "ahis", 0, 0); @ <br> onoff_attribute("Require mouse movement before enabling hyperlinks", "auto-hyperlink-mouseover", "ahmo", 0, 0); @ <br> entry_attribute("Delay before enabling hyperlinks (milliseconds)", 5, "auto-hyperlink-delay", "ah-delay", "10", 0); @ </blockquote> @ <p>Hyperlinks for user "nobody" are normally enabled as soon as the page |
︙ | ︙ | |||
1036 1037 1038 1039 1040 1041 1042 | const char *zRepo = PD("repo", ""); const char *zLogin = PD("login", ""); const char *zPw = PD("pw", ""); const char *zNewName = PD("newname", "New Login Group"); login_check_credentials(); if( !g.perm.Setup ){ | | > | | 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 | const char *zRepo = PD("repo", ""); const char *zLogin = PD("login", ""); const char *zPw = PD("pw", ""); const char *zNewName = PD("newname", "New Login Group"); login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } file_canonical_name(g.zRepositoryName, &fullName, 0); zSelfRepo = fossil_strdup(blob_str(&fullName)); blob_reset(&fullName); if( P("join")!=0 ){ login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg); }else if( P("leave") ){ login_group_leave(&zErrMsg); } style_header("Login Group Configuration"); |
︙ | ︙ | |||
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 | @ </table> @ @ <p><form action="%s(g.zTop)/setup_login_group" method="post"><div> login_insert_csrf_secret(); @ To leave this login group press @ <input type="submit" value="Leave Login Group" name="leave"> @ </form></p> } style_footer(); } /* ** WEBPAGE: setup_timeline */ void setup_timeline(void){ double tmDiff; char zTmDiff[20]; login_check_credentials(); if( !g.perm.Setup ){ | > > > > > > > > > > > > > > > > > > > > > > > > | > | > < > > > > > > > > | | 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 | @ </table> @ @ <p><form action="%s(g.zTop)/setup_login_group" method="post"><div> login_insert_csrf_secret(); @ To leave this login group press @ <input type="submit" value="Leave Login Group" name="leave"> @ </form></p> @ <hr><h2>Implementation Details</h2> @ <p>The following are fields from the CONFIG table related to login-groups, @ provided here for instructional and debugging purposes:</p> @ <table border='1' id='configTab'> @ <thead><tr><th>Config.Name<th>Config.Value<th>Config.mtime</tr></thead><tbody> db_prepare(&q, "SELECT name, value, datetime(mtime,'unixepoch') FROM config" " WHERE name GLOB 'peer-*'" " OR name GLOB 'project-*'" " ORDER BY name"); while( db_step(&q)==SQLITE_ROW ){ @ <tr><td>%h(db_column_text(&q,0))</td> @ <td>%h(db_column_text(&q,1))</td> @ <td>%h(db_column_text(&q,2))</td></tr> } db_finalize(&q); @ </tbody></table> output_table_sorting_javascript("configTab","ttt",1); } style_footer(); } /* ** WEBPAGE: setup_timeline */ void setup_timeline(void){ double tmDiff; char zTmDiff[20]; static const char *const azTimeFormats[] = { "0", "HH:MM", "1", "HH:MM:SS", "2", "YYYY-MM-DD HH:MM", "3", "YYMMDD HH:MM", "4", "(off)" }; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_header("Timeline Display Preferences"); db_begin_transaction(); @ <form action="%s(g.zTop)/setup_timeline" method="post"><div> login_insert_csrf_secret(); @ <hr /> onoff_attribute("Allow block-markup in timeline", "timeline-block-markup", "tbm", 0, 0); @ <p>In timeline displays, check-in comments can be displayed with or @ without block markup (paragraphs, tables, etc.)</p> @ <hr /> onoff_attribute("Plaintext comments on timelines", "timeline-plaintext", "tpt", 0, 0); @ <p>In timeline displays, check-in comments are displayed literally, @ without any wiki or HTML interpretation. (Note: Use CSS to change @ display formatting features such as fonts and line-wrapping behavior.)</p> @ <hr /> onoff_attribute("Use Universal Coordinated Time (UTC)", "timeline-utc", "utc", 1, 0); @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or @ Zulu) instead of in local time. On this server, local time is currently tmDiff = db_double(0.0, "SELECT julianday('now')"); tmDiff = db_double(0.0, "SELECT (julianday(%.17g,'localtime')-julianday(%.17g))*24.0", tmDiff, tmDiff); sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", tmDiff); if( strcmp(zTmDiff, "0.0")==0 ){ @ the same as UTC and so this setting will make no difference in @ the display.</p> }else if( tmDiff<0.0 ){ sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", -tmDiff); @ %s(zTmDiff) hours behind UTC.</p> }else{ @ %s(zTmDiff) hours ahead of UTC.</p> } @ <hr /> multiple_choice_attribute("Per-Item Time Format", "timeline-date-format", "tdf", "0", ArraySize(azTimeFormats)/2, azTimeFormats); @ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown @ in a separate box (using CSS class "timelineDate") whenever the date changes. @ With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats, the complete date @ and time is shown on every timeline entry (using the CSS class "timelineTime").</p> @ <hr /> onoff_attribute("Show version differences by default", "show-version-diffs", "vdiff", 0, 0); @ <p>The version-information pages linked from the timeline can either @ show complete diffs of all file changes, or can just list the names of @ the files that have changed. Users can get to either page by @ clicking. This setting selects the default.</p> @ <hr /> entry_attribute("Max timeline comment length", 6, "timeline-max-comment", "tmc", "0", 0); |
︙ | ︙ | |||
1190 1191 1192 1193 1194 1195 1196 | style_footer(); } /* ** WEBPAGE: setup_settings */ void setup_settings(void){ | | | > > > > | > | | > | | > > > > > > > > > | < | > < | > | > > > > > > > > | 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 | style_footer(); } /* ** WEBPAGE: setup_settings */ void setup_settings(void){ Setting const *pSet; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } (void) aCmdHelp; /* NOTE: Silence compiler warning. */ style_header("Settings"); if(!g.repositoryOpen){ /* Provide read-only access to versioned settings, but only if no repo file was explicitly provided. */ db_open_local(0); } db_begin_transaction(); @ <p>This page provides a simple interface to the "fossil setting" command. @ See the "fossil help setting" output below for further information on @ the meaning of each setting.</p><hr /> @ <form action="%s(g.zTop)/setup_settings" method="post"><div> @ <table border="0"><tr><td valign="top"> login_insert_csrf_secret(); for(pSet=aSetting; pSet->name!=0; pSet++){ if( pSet->width==0 ){ int hasVersionableValue = pSet->versionable && (db_get_versioned(pSet->name, NULL)!=0); onoff_attribute(pSet->name, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, is_truth(pSet->def), hasVersionableValue); if( pSet->versionable ){ @ (v)<br /> } else { @ <br /> } } } @ <br /><input type="submit" name="submit" value="Apply Changes" /> @ </td><td style="width:50px;"></td><td valign="top"> for(pSet=aSetting; pSet->name!=0; pSet++){ if( pSet->width!=0 && !pSet->versionable && !pSet->forceTextArea ){ entry_attribute(pSet->name, /*pSet->width*/ 25, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, (char*)pSet->def, 0); @ <br /> } } for(pSet=aSetting; pSet->name!=0; pSet++){ if( pSet->width!=0 && !pSet->versionable && pSet->forceTextArea ){ @<b>%s(pSet->name)</b><br /> textarea_attribute("", /*rows*/ 3, /*cols*/ 50, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, (char*)pSet->def, 0); @ <br /> } } @ </td><td style="width:50px;"></td><td valign="top"> for(pSet=aSetting; pSet->name!=0; pSet++){ if( pSet->width!=0 && pSet->versionable ){ int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; @<b>%s(pSet->name)</b> (v)<br /> textarea_attribute("", /*rows*/ 3, /*cols*/ 20, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, (char*)pSet->def, hasVersionableValue); @<br /> } } @ </td></tr></table> @ </div></form> @ <p>Settings marked with (v) are 'versionable' and will be overridden @ by the contents of files named <tt>.fossil-settings/PROPERTY</tt>. @ If such a file is present, the corresponding field above is not @ editable.</p><hr /><p> @ These settings work in the same way, as the <kbd>set</kbd> @ commandline:<br /> @ </p><pre>%s(zHelp_setting_cmd)</pre> db_end_transaction(0); style_footer(); } /* ** WEBPAGE: setup_config */ void setup_config(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_header("WWW Configuration"); db_begin_transaction(); @ <form action="%s(g.zTop)/setup_config" method="post"><div> login_insert_csrf_secret(); @ <hr /> entry_attribute("Project Name", 60, "project-name", "pn", "", 0); @ <p>Give your project a name so visitors know what this site is about. @ The project name will also be used as the RSS feed title. @ </p> @ <hr /> textarea_attribute("Project Description", 3, 80, "project-description", "pd", "", 0); @ <p>Describe your project. This will be used in page headers for search @ engines as well as a short RSS description.</p> @ <hr /> entry_attribute("Tarball and ZIP-archive Prefix", 20, "short-project-name", "spn", "", 0); @ <p>This is used as a prefix on the names of generated tarballs and ZIP archive. @ For best results, keep this prefix brief and avoid special characters such @ as "/" and "\". @ If no tarball prefix is specified, then the full Project Name above is used. @ </p> @ <hr /> onoff_attribute("Enable WYSIWYG Wiki Editing", "wysiwyg-wiki", "wysiwyg-wiki", 0, 0); @ <p>Enable what-you-see-is-what-you-get (WYSIWYG) editing of wiki pages. @ The WYSIWYG editor generates HTML instead of markup, which makes @ subsequent manual editing more difficult.</p> @ <hr /> |
︙ | ︙ | |||
1325 1326 1327 1328 1329 1330 1331 1332 | @ <hr /> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ </div></form> db_end_transaction(0); style_footer(); } /* | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > | | | | 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 | @ <hr /> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ </div></form> db_end_transaction(0); style_footer(); } /* ** WEBPAGE: setup_modreq */ void setup_modreq(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_header("Moderator For Wiki And Tickets"); db_begin_transaction(); @ <form action="%R/setup_modreq" method="post"><div> login_insert_csrf_secret(); @ <hr /> onoff_attribute("Moderate ticket changes", "modreq-tkt", "modreq-tkt", 0, 0); @ <p>When enabled, any change to tickets is subject to the approval @ by a ticket moderator - a user with the "q" or Mod-Tkt privilege. @ Ticket changes enter the system and are shown locally, but are not @ synced until they are approved. The moderator has the option to @ delete the change rather than approve it. Ticket changes made by @ a user who has the Mod-Tkt privilege are never subject to @ moderation. @ @ <hr /> onoff_attribute("Moderate wiki changes", "modreq-wiki", "modreq-wiki", 0, 0); @ <p>When enabled, any change to wiki is subject to the approval @ by a wiki moderator - a user with the "l" or Mod-Wiki privilege. @ Wiki changes enter the system and are shown locally, but are not @ synced until they are approved. The moderator has the option to @ delete the change rather than approve it. Wiki changes made by @ a user who has the Mod-Wiki privilege are never subject to @ moderation. @ </p> |
︙ | ︙ | |||
1515 1516 1517 1518 1519 1520 1521 | /* ** WEBPAGE: setup_adunit */ void setup_adunit(void){ login_check_credentials(); if( !g.perm.Setup ){ | | > | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 | /* ** WEBPAGE: setup_adunit */ void setup_adunit(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } db_begin_transaction(); if( P("clear")!=0 ){ db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'"); cgi_replace_parameter("adunit",""); } style_header("Edit Ad Unit"); @ <form action="%s(g.zTop)/setup_adunit" method="post"><div> login_insert_csrf_secret(); @ <b>Banner Ad-Unit:</b><br /> textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); @ <br /> @ <b>Right-Column Ad-Unit:</b><br /> textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); @ <br /> onoff_attribute("Omit ads to administrator", "adunit-omit-if-admin", "oia", 0, 0); @ <br /> onoff_attribute("Omit ads to logged-in users", "adunit-omit-if-user", "oiu", 0, 0); @ <br /> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Delete Ad-Unit" /> @ </div></form> @ <hr /> @ <b>Ad-Unit Notes:</b><ul> @ <li>Leave both Ad-Units blank to disable all advertising. @ <li>The "Banner Ad-Unit" is used for wide pages. @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is used on all pages. @ <li>Suggested <a href="setup_skinedit?w=0">CSS</a> changes: @ <blockquote><pre> @ div.adunit_banner { @ margin: auto; @ width: 100%; @ } @ div.adunit_right { @ float: right; @ } @ div.adunit_right_container { @ min-height: <i>height-of-right-column-ad-unit</i>; @ } @ </pre></blockquote> @ <li>For a place-holder Ad-Unit for testing, Copy/Paste the following @ with appropriate adjustments to "width:" and "height:". @ <blockquote><pre> @ <div style=' @ margin: 0 auto; @ width: 600px; @ height: 90px; @ border: 1px solid #f11; @ background-color: #fcc; @ '>Demo Ad</div> @ </pre></blockquote> @ </li> style_footer(); db_end_transaction(0); } /* ** WEBPAGE: setup_logo */ void setup_logo(void){ const char *zLogoMtime = db_get_mtime("logo-image", 0, 0); const char *zLogoMime = db_get("logo-mimetype","image/gif"); const char *aLogoImg = P("logoim"); int szLogoImg = atoi(PD("logoim:bytes","0")); const char *zBgMtime = db_get_mtime("background-image", 0, 0); const char *zBgMime = db_get("background-mimetype","image/gif"); const char *aBgImg = P("bgim"); int szBgImg = atoi(PD("bgim:bytes","0")); if( szLogoImg>0 ){ zLogoMime = PD("logoim:mimetype","image/gif"); } if( szBgImg>0 ){ zBgMime = PD("bgim:mimetype","image/gif"); } login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } db_begin_transaction(); if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){ Blob img; Stmt ins; blob_init(&img, aLogoImg, szLogoImg); db_prepare(&ins, |
︙ | ︙ | |||
1617 1618 1619 1620 1621 1622 1623 | ); db_end_transaction(0); cgi_redirect("setup_logo"); } style_header("Edit Project Logo And Background"); @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> @ and looks like this:</p> | | | | | | | | 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 | ); db_end_transaction(0); cgi_redirect("setup_logo"); } style_header("Edit Project Logo And Background"); @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b> @ and looks like this:</p> @ <blockquote><p><img src="%s(g.zTop)/logo/%z(zLogoMtime)" alt="logo" border="1" /> @ </p></blockquote> @ @ <form action="%s(g.zTop)/setup_logo" method="post" @ enctype="multipart/form-data"><div> @ <p>The logo is accessible to all users at this URL: @ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>. @ The logo may or may not appear on each @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and @ <a href="setup_skinedit?w=2">header setup</a>. @ To change the logo image, use the following form:</p> login_insert_csrf_secret(); @ Logo Image file: @ <input type="file" name="logoim" size="60" accept="image/*" /> @ <p align="center"> @ <input type="submit" name="setlogo" value="Change Logo" /> @ <input type="submit" name="clrlogo" value="Revert To Default" /></p> @ </div></form> @ <hr /> @ @ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b> @ and looks like this:</p> @ <blockquote><p><img src="%s(g.zTop)/background/%z(zBgMtime)" alt="background" border=1 /> @ </p></blockquote> @ @ <form action="%s(g.zTop)/setup_logo" method="post" @ enctype="multipart/form-data"><div> @ <p>The background image is accessible to all users at this URL: @ <a href="%s(g.zBaseURL)/background">%s(g.zBaseURL)/background</a>. @ The background image may or may not appear on each @ page depending on the <a href="setup_skinedit?w=0">CSS</a> and @ <a href="setup_skinedit?w=2">header setup</a>. @ To change the background image, use the following form:</p> login_insert_csrf_secret(); @ Background image file: @ <input type="file" name="bgim" size="60" accept="image/*" /> @ <p align="center"> @ <input type="submit" name="setbg" value="Change Background" /> @ <input type="submit" name="clrbg" value="Revert To Default" /></p> |
︙ | ︙ | |||
1700 1701 1702 1703 1704 1705 1706 | ** Run raw SQL commands against the database file using the web interface. */ void sql_page(void){ const char *zQ = P("q"); int go = P("go")!=0; login_check_credentials(); if( !g.perm.Setup ){ | | > | 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 | ** Run raw SQL commands against the database file using the web interface. */ void sql_page(void){ const char *zQ = P("q"); int go = P("go")!=0; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } db_begin_transaction(); style_header("Raw SQL Commands"); @ <p><b>Caution:</b> There are no restrictions on the SQL that can be @ run by this page. You can do serious and irrepairable damage to the @ repository. Proceed with extreme caution.</p> @ |
︙ | ︙ | |||
1821 1822 1823 1824 1825 1826 1827 | ** may be run as well. */ void th1_page(void){ const char *zQ = P("q"); int go = P("go")!=0; login_check_credentials(); if( !g.perm.Setup ){ | | > | 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 | ** may be run as well. */ void th1_page(void){ const char *zQ = P("q"); int go = P("go")!=0; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } db_begin_transaction(); style_header("Raw TH1 Commands"); @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be @ run by this page. If Tcl integration was enabled at compile-time and @ the "tcl" setting is enabled, Tcl commands may be run as well.</p> @ |
︙ | ︙ | |||
1851 1852 1853 1854 1855 1856 1857 | @ <pre class="th1result">%h(zR)</pre> }else{ @ <pre class="th1error">%h(zR)</pre> } } style_footer(); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 | @ <pre class="th1result">%h(zR)</pre> }else{ @ <pre class="th1error">%h(zR)</pre> } } style_footer(); } static void admin_log_render_limits(){ int const count = db_int(0,"SELECT COUNT(*) FROM admin_log"); int i; int limits[] = { 10, 20, 50, 100, 250, 500, 0 }; for(i = 0; limits[i]; ++i ){ cgi_printf("%s<a href='?n=%d'>%d</a>", i ? " " : "", limits[i], limits[i]); if(limits[i]>count) break; } } /* ** WEBPAGE: admin_log ** ** Shows the contents of the admin_log table, which is only created if ** the admin-log setting is enabled. Requires Admin or Setup ('a' or ** 's') permissions. */ void page_admin_log(){ Stmt stLog = empty_Stmt; Blob qLog = empty_blob; int limit; int fLogEnabled; int counter = 0; login_check_credentials(); if( !g.perm.Setup && !g.perm.Admin ){ login_needed(0); return; } style_header("Admin Log"); create_admin_log_table(); limit = atoi(PD("n","20")); fLogEnabled = db_get_boolean("admin-log", 0); @ <div>Admin logging is %s(fLogEnabled?"on":"off").</div> @ <div>Limit results to: <span> admin_log_render_limits(); @ </span></div> blob_append_sql(&qLog, "SELECT datetime(time,'unixepoch'), who, page, what " "FROM admin_log " "ORDER BY time DESC "); if(limit>0){ @ %d(limit) Most recent entries: blob_append_sql(&qLog, "LIMIT %d", limit); } db_prepare(&stLog, "%s", blob_sql_text(&qLog)); blob_reset(&qLog); @ <table id="adminLogTable" class="adminLogTable" width="100%%"> @ <thead> @ <th>Time</th> @ <th>User</th> @ <th>Page</th> @ <th width="60%%">Message</th> @ </thead><tbody> while( SQLITE_ROW == db_step(&stLog) ){ const char *zTime = db_column_text(&stLog, 0); const char *zUser = db_column_text(&stLog, 1); const char *zPage = db_column_text(&stLog, 2); const char *zMessage = db_column_text(&stLog, 3); @ <tr class="row%d(counter++%2)"> @ <td class="adminTime">%s(zTime)</td> @ <td>%s(zUser)</td> @ <td>%s(zPage)</td> @ <td>%h(zMessage)</td> @ </tr> } @ </tbody></table> if(limit>0 && counter<limit){ @ <div>%d(counter) entries shown.</div> } style_footer(); } /* ** WEBPAGE: srchsetup ** ** Configure the search engine. */ void page_srchsetup(){ login_check_credentials(); if( !g.perm.Setup && !g.perm.Admin ){ login_needed(0); return; } style_header("Search Configuration"); @ <form action="%s(g.zTop)/srchsetup" method="post"><div> login_insert_csrf_secret(); @ <div style="text-align:center;font-weight:bold;"> @ Server-specific settings that affect the @ <a href="%R/search">/search</a> webpage. @ </div> @ <hr /> textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0); @ <p>The "Document Glob List" is a comma- or newline-separated list @ of GLOB expressions that identify all documents within the source @ tree that are to be searched when "Document Search" is enabled. @ Some examples: @ <table border=0 cellpadding=2 align=center> @ <tr><td>*.wiki,*.html,*.md,*.txt<td style="width: 4x;"> @ <td>Search all wiki, HTML, Markdown, and Text files</tr> @ <tr><td>doc/*.md,*/README.txt,README.txt<td> @ <td>Search all Markdown files in the doc/ subfolder and all README.txt @ files.</tr> @ <tr><td>*<td><td>Search all checked-in files</tr> @ <tr><td><i>(blank)</i><td> @ <td>Search nothing. (Disables document search).</tr> @ </table> @ <hr /> entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0); @ <p>When searching documents, use the versions of the files found at the @ type of the "Document Branch" branch. Recommended value: "trunk". @ Document search is disabled if blank. @ <hr/> onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0); @ <br> onoff_attribute("Search Documents", "search-doc", "sd", 0, 0); @ <br> onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0); @ <br> onoff_attribute("Search Wiki","search-wiki", "sw", 0, 0); @ <hr/> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ <hr/> if( P("fts0") ){ search_drop_index(); }else if( P("fts1") ){ search_drop_index(); search_create_index(); search_fill_index(); search_update_index(search_restrict(SRCH_ALL)); } if( search_index_exists() ){ @ <p>Currently using an SQLite FTS4 search index. This makes search @ run faster, especially on large repositories, but takes up space.</p> onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); @ <p><input type="submit" name="fts0" value="Delete The Full-Text Index"> @ <input type="submit" name="fts1" value="Rebuild The Full-Text Index"> }else{ @ <p>The SQLite FTS4 search index is disabled. All searching will be @ a full-text scan. This usually works fine, but can be slow for @ larger repositories.</p> onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0); @ <p><input type="submit" name="fts1" value="Create A Full-Text Index"> } @ </div></form> style_footer(); } |
Changes to src/sha1.c.
1 2 3 4 | /* ** This implementation of SHA1. */ #include <sys/types.h> | > | > > > | > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /* ** This implementation of SHA1. */ #include "config.h" #include <sys/types.h> #include "sha1.h" #ifdef FOSSIL_ENABLE_SSL # include <openssl/sha.h> # define SHA1Context SHA_CTX # define SHA1Init SHA1_Init # define SHA1Update SHA1_Update # define SHA1Final(a,b) SHA1_Final(b,a) #else /* ** The SHA1 implementation below is adapted from: ** ** $NetBSD: sha1.c,v 1.6 2009/11/06 20:31:18 joerg Exp $ ** $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ ** |
︙ | ︙ | |||
53 54 55 56 57 58 59 | #define blk0be(i) block[i] #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ ^block[(i+2)&15]^block[i&15],1)) /* * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 * | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #define blk0be(i) block[i] #define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ ^block[(i+2)&15]^block[i&15],1)) /* * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 * * Rl0() for little-endian and Rb0() for big-endian. Endianness is * determined at run-time. */ #define Rl0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); #define Rb0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2); #define R1(v,w,x,y,z,i) \ |
︙ | ︙ | |||
159 160 161 162 163 164 165 | const unsigned char *data, unsigned int len ){ unsigned int i, j; j = context->count[0]; if ((context->count[0] += len << 3) < j) | | | | | | | | | | | | | | > | | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | const unsigned char *data, unsigned int len ){ unsigned int i, j; j = context->count[0]; if ((context->count[0] += len << 3) < j) context->count[1] += (len>>29)+1; j = (j >> 3) & 63; if ((j + len) > 63) { (void)memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) SHA1Transform(context->state, &data[i]); j = 0; } else { i = 0; } (void)memcpy(&context->buffer[j], &data[i], len - i); } /* * Add padding and return the message digest. */ static void SHA1Final(SHA1Context *context, unsigned char digest[20]){ unsigned int i; unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1Update(context, (const unsigned char *)"\200", 1); while ((context->count[0] & 504) != 448) SHA1Update(context, (const unsigned char *)"\0", 1); SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ if (digest) { for (i = 0; i < 20; i++) digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } } #endif /* ** Convert a digest into base-16. digest should be declared as ** "unsigned char digest[20]" in the calling function. The SHA1 ** digest is stored in the first 20 bytes. zBuf should ** be "char zBuf[41]". */ static void DigestToBase16(unsigned char *digest, char *zBuf){ static const char zEncode[] = "0123456789abcdef"; int ix; for(ix=0; ix<20; ix++){ *zBuf++ = zEncode[(*digest>>4)&0xf]; *zBuf++ = zEncode[*digest++ & 0xf]; } *zBuf = '\0'; |
︙ | ︙ | |||
246 247 248 249 250 251 252 | */ void sha1sum_step_blob(Blob *p){ sha1sum_step_text(blob_buffer(p), blob_size(p)); } /* ** Finish the incremental SHA1 checksum. Store the result in blob pOut | | | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | */ void sha1sum_step_blob(Blob *p){ sha1sum_step_text(blob_buffer(p), blob_size(p)); } /* ** Finish the incremental SHA1 checksum. Store the result in blob pOut ** if pOut!=0. Also return a pointer to the result. ** ** This resets the incremental checksum preparing for the next round ** of computation. The return pointer points to a static buffer that ** is overwritten by subsequent calls to this function. */ char *sha1sum_finish(Blob *pOut){ unsigned char zResult[20]; |
︙ | ︙ | |||
283 284 285 286 287 288 289 | unsigned char zResult[20]; char zBuf[10240]; if( file_wd_islink(zFilename) ){ /* Instead of file content, return sha1 of link destination path */ Blob destinationPath; int rc; | | | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | unsigned char zResult[20]; char zBuf[10240]; if( file_wd_islink(zFilename) ){ /* Instead of file content, return sha1 of link destination path */ Blob destinationPath; int rc; blob_read_link(&destinationPath, zFilename); rc = sha1sum_blob(&destinationPath, pCksum); blob_reset(&destinationPath); return rc; } in = fossil_fopen(zFilename,"rb"); |
︙ | ︙ | |||
351 352 353 354 355 356 357 | SHA1Final(&ctx, zResult); DigestToBase16(zResult, zDigest); return mprintf("%s", zDigest); } /* ** Convert a cleartext password for a specific user into a SHA1 hash. | | | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | SHA1Final(&ctx, zResult); DigestToBase16(zResult, zDigest); return mprintf("%s", zDigest); } /* ** Convert a cleartext password for a specific user into a SHA1 hash. ** ** The algorithm here is: ** ** SHA1( project-code + "/" + login + "/" + password ) ** ** In words: The users login name and password are appended to the ** project ID code and the SHA1 hash of the result is computed. ** ** The result of this function is the shared secret used by a client ** to authenticate to a server for the sync protocol. It is also the ** value stored in the USER.PW field of the database. By mixing in the ** login name and the project id with the hash, different shared secrets ** are obtained even if two users select the same password, or if a ** single user selects the same password for multiple projects. */ char *sha1_shared_secret( const char *zPw, /* The password to encrypt */ const char *zLogin, /* Username */ const char *zProjCode /* Project-code. Use built-in project code if NULL */ ){ |
︙ | ︙ | |||
445 446 447 448 449 450 451 | ** Compute an SHA1 checksum of all files named on the command-line. ** If an file is named "-" then take its content from standard input. */ void sha1sum_test(void){ int i; Blob in; Blob cksum; | | | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | ** Compute an SHA1 checksum of all files named on the command-line. ** If an file is named "-" then take its content from standard input. */ void sha1sum_test(void){ int i; Blob in; Blob cksum; for(i=2; i<g.argc; i++){ blob_init(&cksum, "************** not found ***************", -1); if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ blob_read_from_channel(&in, stdin, -1); sha1sum_blob(&in, &cksum); }else{ sha1sum_file(g.argv[i], &cksum); } fossil_print("%s %s\n", blob_str(&cksum), g.argv[i]); blob_reset(&cksum); } } |
Changes to src/shell.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | ** utility for accessing SQLite databases. */ #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) /* This needs to come before any includes for MSVC compiler */ #define _CRT_SECURE_NO_WARNINGS #endif /* ** Enable large-file support for fopen() and friends on unix. */ #ifndef SQLITE_DISABLE_LFS # define _LARGE_FILE 1 # ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 # endif # define _LARGEFILE_SOURCE 1 #endif #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> #include "sqlite3.h" #include <ctype.h> #include <stdarg.h> #if !defined(_WIN32) && !defined(WIN32) # include <signal.h> # if !defined(__RTP__) && !defined(_WRS_KERNEL) # include <pwd.h> # endif # include <unistd.h> # include <sys/types.h> #endif | > > > > > > > > > > > > > > > > > > > > > > | | | > | > > > > > | > | > > > > > > | > | | | | > > > > > | > > | | | | > > > > < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > > > > > > > > | > > > | > > | > | | | > | | > > > | | > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | ** utility for accessing SQLite databases. */ #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) /* This needs to come before any includes for MSVC compiler */ #define _CRT_SECURE_NO_WARNINGS #endif /* ** If requested, include the SQLite compiler options file for MSVC. */ #if defined(INCLUDE_MSVC_H) #include "msvc.h" #endif /* ** No support for loadable extensions in VxWorks. */ #if (defined(__RTP__) || defined(_WRS_KERNEL)) && !SQLITE_OMIT_LOAD_EXTENSION # define SQLITE_OMIT_LOAD_EXTENSION 1 #endif /* ** Enable large-file support for fopen() and friends on unix. */ #ifndef SQLITE_DISABLE_LFS # define _LARGE_FILE 1 # ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 # endif # define _LARGEFILE_SOURCE 1 #endif #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> #include "sqlite3.h" #if SQLITE_USER_AUTHENTICATION # include "sqlite3userauth.h" #endif #include <ctype.h> #include <stdarg.h> #if !defined(_WIN32) && !defined(WIN32) # include <signal.h> # if !defined(__RTP__) && !defined(_WRS_KERNEL) # include <pwd.h> # endif # include <unistd.h> # include <sys/types.h> #endif #if HAVE_READLINE # include <readline/readline.h> # include <readline/history.h> #endif #if HAVE_EDITLINE # include <editline/readline.h> #endif #if HAVE_EDITLINE || HAVE_READLINE # define shell_add_history(X) add_history(X) # define shell_read_history(X) read_history(X) # define shell_write_history(X) write_history(X) # define shell_stifle_history(X) stifle_history(X) # define shell_readline(X) readline(X) #elif HAVE_LINENOISE # include "linenoise.h" # define shell_add_history(X) linenoiseHistoryAdd(X) # define shell_read_history(X) linenoiseHistoryLoad(X) # define shell_write_history(X) linenoiseHistorySave(X) # define shell_stifle_history(X) linenoiseHistorySetMaxLen(X) # define shell_readline(X) linenoise(X) #else # define shell_read_history(X) # define shell_write_history(X) # define shell_stifle_history(X) # define SHELL_USE_LOCAL_GETLINE 1 #endif #if defined(_WIN32) || defined(WIN32) # include <io.h> # include <fcntl.h> #define isatty(h) _isatty(h) #ifndef access # define access(f,m) _access((f),(m)) #endif #undef popen #define popen _popen #undef pclose #define pclose _pclose #else /* Make sure isatty() has a prototype. */ extern int isatty(int); #if !defined(__RTP__) && !defined(_WRS_KERNEL) /* popen and pclose are not C89 functions and so are sometimes omitted from ** the <stdio.h> header */ extern FILE *popen(const char*,const char*); extern int pclose(FILE*); #else # define SQLITE_OMIT_POPEN 1 #endif #endif #if defined(_WIN32_WCE) /* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() * thus we always assume that we have a console. That can be * overridden with the -batch command line option. */ #define isatty(x) 1 #endif /* ctype macros that work with signed characters */ #define IsSpace(X) isspace((unsigned char)X) #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) /* On Windows, we normally run with output mode of TEXT so that \n characters ** are automatically translated into \r\n. However, this behavior needs ** to be disabled in some cases (ex: when generating CSV output and when ** rendering quoted strings that contain \n characters). The following ** routines take care of that. */ #if defined(_WIN32) || defined(WIN32) static void setBinaryMode(FILE *out){ fflush(out); _setmode(_fileno(out), _O_BINARY); } static void setTextMode(FILE *out){ fflush(out); _setmode(_fileno(out), _O_TEXT); } #else # define setBinaryMode(X) # define setTextMode(X) #endif /* True if the timer is enabled */ static int enableTimer = 0; /* Return the current wall-clock time */ static sqlite3_int64 timeOfDay(void){ static sqlite3_vfs *clockVfs = 0; sqlite3_int64 t; if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); if( clockVfs->iVersion>=1 && clockVfs->xCurrentTimeInt64!=0 ){ clockVfs->xCurrentTimeInt64(clockVfs, &t); }else{ double r; clockVfs->xCurrentTime(clockVfs, &r); t = (sqlite3_int64)(r*86400000.0); } return t; } #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) #include <sys/time.h> #include <sys/resource.h> /* VxWorks does not support getrusage() as far as we can determine */ #if defined(_WRS_KERNEL) || defined(__RTP__) struct rusage { struct timeval ru_utime; /* user CPU time used */ struct timeval ru_stime; /* system CPU time used */ }; #define getrusage(A,B) memset(B,0,sizeof(*B)) #endif /* Saved resource information for the beginning of an operation */ static struct rusage sBegin; /* CPU time at start */ static sqlite3_int64 iBegin; /* Wall-clock time at start */ /* ** Begin timing an operation */ static void beginTimer(void){ if( enableTimer ){ getrusage(RUSAGE_SELF, &sBegin); iBegin = timeOfDay(); } } /* Return the difference of two time_structs in seconds */ static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + (double)(pEnd->tv_sec - pStart->tv_sec); } /* ** Print the timing results. */ static void endTimer(void){ if( enableTimer ){ sqlite3_int64 iEnd = timeOfDay(); struct rusage sEnd; getrusage(RUSAGE_SELF, &sEnd); printf("Run Time: real %.3f user %f sys %f\n", (iEnd - iBegin)*0.001, timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); } } #define BEGIN_TIMER beginTimer() #define END_TIMER endTimer() #define HAS_TIMER 1 #elif (defined(_WIN32) || defined(WIN32)) #include <windows.h> /* Saved resource information for the beginning of an operation */ static HANDLE hProcess; static FILETIME ftKernelBegin; static FILETIME ftUserBegin; static sqlite3_int64 ftWallBegin; typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); static GETPROCTIMES getProcessTimesAddr = NULL; /* ** Check to see if we have timer support. Return 1 if necessary ** support found (or found previously). */ static int hasTimer(void){ if( getProcessTimesAddr ){ return 1; } else { /* GetProcessTimes() isn't supported in WIN95 and some other Windows ** versions. See if the version we are running on has it, and if it ** does, save off a pointer to it and the current process handle. */ hProcess = GetCurrentProcess(); if( hProcess ){ HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); if( NULL != hinstLib ){ getProcessTimesAddr = (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); if( NULL != getProcessTimesAddr ){ return 1; } FreeLibrary(hinstLib); } } } return 0; } /* ** Begin timing an operation */ static void beginTimer(void){ if( enableTimer && getProcessTimesAddr ){ FILETIME ftCreation, ftExit; getProcessTimesAddr(hProcess,&ftCreation,&ftExit, &ftKernelBegin,&ftUserBegin); ftWallBegin = timeOfDay(); } } /* Return the difference of two FILETIME structs in seconds */ static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ sqlite_int64 i64Start = *((sqlite_int64 *) pStart); sqlite_int64 i64End = *((sqlite_int64 *) pEnd); return (double) ((i64End - i64Start) / 10000000.0); } /* ** Print the timing results. */ static void endTimer(void){ if( enableTimer && getProcessTimesAddr){ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; sqlite3_int64 ftWallEnd = timeOfDay(); getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); printf("Run Time: real %.3f user %f sys %f\n", (ftWallEnd - ftWallBegin)*0.001, timeDiff(&ftUserBegin, &ftUserEnd), timeDiff(&ftKernelBegin, &ftKernelEnd)); } } #define BEGIN_TIMER beginTimer() #define END_TIMER endTimer() |
︙ | ︙ | |||
264 265 266 267 268 269 270 | /* ** This routine works like printf in that its first argument is a ** format string and subsequent arguments are values to be substituted ** in place of % fields. The result of formatting this string ** is written to iotrace. */ #ifdef SQLITE_ENABLE_IOTRACE | | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | /* ** This routine works like printf in that its first argument is a ** format string and subsequent arguments are values to be substituted ** in place of % fields. The result of formatting this string ** is written to iotrace. */ #ifdef SQLITE_ENABLE_IOTRACE static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ va_list ap; char *z; if( iotrace==0 ) return; va_start(ap, zFormat); z = sqlite3_vmprintf(zFormat, ap); va_end(ap); fprintf(iotrace, "%s", z); |
︙ | ︙ | |||
385 386 387 388 389 390 391 | static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ char *zPrompt; char *zResult; if( in!=0 ){ zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; | | < < < < > > > > > > > > > | | | | | > | < < > | > > > > > | | < | < > > > > > > > > > > > > > > > > > > > > > > > > > > | | 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 | static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ char *zPrompt; char *zResult; if( in!=0 ){ zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; #if SHELL_USE_LOCAL_GETLINE printf("%s", zPrompt); fflush(stdout); zResult = local_getline(zPrior, stdin); #else free(zPrior); zResult = shell_readline(zPrompt); if( zResult && *zResult ) shell_add_history(zResult); #endif } return zResult; } /* ** Shell output mode information from before ".explain on", ** saved so that it can be restored by ".explain off" */ typedef struct SavedModeInfo SavedModeInfo; struct SavedModeInfo { int valid; /* Is there legit data in here? */ int mode; /* Mode prior to ".explain on" */ int showHeader; /* The ".header" setting prior to ".explain on" */ int colWidth[100]; /* Column widths prior to ".explain on" */ }; /* ** State information about the database connection is contained in an ** instance of the following structure. */ typedef struct ShellState ShellState; struct ShellState { sqlite3 *db; /* The database */ int echoOn; /* True to echo input commands */ int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ int statsOn; /* True to display memory stats before each finalize */ int scanstatsOn; /* True to display scan stats before each finalize */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ int writableSchema; /* True if PRAGMA writable_schema=ON */ int showHeader; /* True to show column names in List or Column mode */ unsigned shellFlgs; /* Various flags */ char *zDestTable; /* Name of destination table when MODE_Insert */ char colSeparator[20]; /* Column separator character for several modes */ char rowSeparator[20]; /* Row separator character for MODE_Ascii */ int colWidth[100]; /* Requested width of each column when in column mode*/ int actualWidth[100]; /* Actual width of each column */ char nullValue[20]; /* The text to print when a NULL comes back from ** the database */ SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ char *zFreeOnClose; /* Filename to free when closing */ const char *zVfs; /* Name of VFS to use */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ int iIndent; /* Index of current op in aiIndent[] */ }; /* ** These are the allowed shellFlgs values */ #define SHFLG_Scratch 0x00001 /* The --scratch option is used */ #define SHFLG_Pagecache 0x00002 /* The --pagecache option is used */ #define SHFLG_Lookaside 0x00004 /* Lookaside memory is used */ /* ** These are the allowed modes. */ #define MODE_Line 0 /* One column per line. Blank line between records */ #define MODE_Column 1 /* One record per line in neat columns */ #define MODE_List 2 /* One record per line with a separator */ #define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ #define MODE_Html 4 /* Generate an XHTML table */ #define MODE_Insert 5 /* Generate SQL "insert" statements */ #define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Csv 7 /* Quote strings, numbers are plain */ #define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ #define MODE_Ascii 9 /* Use ASCII unit and record separators (0x1F/0x1E) */ static const char *modeDescr[] = { "line", "column", "list", "semi", "html", "insert", "tcl", "csv", "explain", "ascii", }; /* ** These are the column/row/line separators used by the various ** import/export modes. */ #define SEP_Column "|" #define SEP_Row "\n" #define SEP_Tab "\t" #define SEP_Space " " #define SEP_Comma "," #define SEP_CrLf "\r\n" #define SEP_Unit "\x1F" #define SEP_Record "\x1E" /* ** Number of elements in an array */ #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. */ static int strlen30(const char *z){ const char *z2 = z; while( *z2 ){ z2++; } return 0x3fffffff & (int)(z2 - z); } /* ** A callback for the sqlite3_log() interface. */ static void shellLog(void *pArg, int iErrCode, const char *zMsg){ ShellState *p = (ShellState*)pArg; if( p->pLog==0 ) return; fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg); fflush(p->pLog); } /* ** Output the given string as a hex-encoded blob (eg. X'1234' ) |
︙ | ︙ | |||
504 505 506 507 508 509 510 511 512 513 514 515 516 517 | /* ** Output the given string as a quoted string using SQL quoting conventions. */ static void output_quoted_string(FILE *out, const char *z){ int i; int nSingle = 0; for(i=0; z[i]; i++){ if( z[i]=='\'' ) nSingle++; } if( nSingle==0 ){ fprintf(out,"'%s'",z); }else{ fprintf(out,"'"); | > | 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 | /* ** Output the given string as a quoted string using SQL quoting conventions. */ static void output_quoted_string(FILE *out, const char *z){ int i; int nSingle = 0; setBinaryMode(out); for(i=0; z[i]; i++){ if( z[i]=='\'' ) nSingle++; } if( nSingle==0 ){ fprintf(out,"'%s'",z); }else{ fprintf(out,"'"); |
︙ | ︙ | |||
526 527 528 529 530 531 532 533 534 535 536 537 538 539 | }else{ fprintf(out,"%s",z); break; } } fprintf(out,"'"); } } /* ** Output the given string as a quoted according to C or TCL quoting rules. */ static void output_c_string(FILE *out, const char *z){ unsigned int c; | > | 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | }else{ fprintf(out,"%s",z); break; } } fprintf(out,"'"); } setTextMode(out); } /* ** Output the given string as a quoted according to C or TCL quoting rules. */ static void output_c_string(FILE *out, const char *z){ unsigned int c; |
︙ | ︙ | |||
565 566 567 568 569 570 571 572 573 574 575 576 577 578 | /* ** Output the given string with characters that are special to ** HTML escaped. */ static void output_html_string(FILE *out, const char *z){ int i; while( *z ){ for(i=0; z[i] && z[i]!='<' && z[i]!='&' && z[i]!='>' && z[i]!='\"' && z[i]!='\''; | > | 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 | /* ** Output the given string with characters that are special to ** HTML escaped. */ static void output_html_string(FILE *out, const char *z){ int i; if( z==0 ) z = ""; while( *z ){ for(i=0; z[i] && z[i]!='<' && z[i]!='&' && z[i]!='>' && z[i]!='\"' && z[i]!='\''; |
︙ | ︙ | |||
617 618 619 620 621 622 623 | 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, 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, }; /* | | | | > | | | | | | | > | > > > > > > | | | | | | > | > | | < > > > > > > | > | > | > | | | | | | | | | | > | | | | | | > > | > > > > > > > > > > > > > > > > > | | | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 | 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, 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, }; /* ** Output a single term of CSV. Actually, p->colSeparator is used for ** the separator, which may or may not be a comma. p->nullValue is ** the null value. Strings are quoted if necessary. The separator ** is only issued if bSep is true. */ static void output_csv(ShellState *p, const char *z, int bSep){ FILE *out = p->out; if( z==0 ){ fprintf(out,"%s",p->nullValue); }else{ int i; int nSep = strlen30(p->colSeparator); for(i=0; z[i]; i++){ if( needCsvQuote[((unsigned char*)z)[i]] || (z[i]==p->colSeparator[0] && (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ i = 0; break; } } if( i==0 ){ putc('"', out); for(i=0; z[i]; i++){ if( z[i]=='"' ) putc('"', out); putc(z[i], out); } putc('"', out); }else{ fprintf(out, "%s", z); } } if( bSep ){ fprintf(p->out, "%s", p->colSeparator); } } #ifdef SIGINT /* ** This routine runs when the user presses Ctrl-C */ static void interrupt_handler(int NotUsed){ UNUSED_PARAMETER(NotUsed); seenInterrupt++; if( seenInterrupt>2 ) exit(1); if( db ) sqlite3_interrupt(db); } #endif /* ** This is the callback routine that the shell ** invokes for each row of a query result. */ static int shell_callback( void *pArg, int nArg, /* Number of result columns */ char **azArg, /* Text of each result column */ char **azCol, /* Column names */ int *aiType /* Column types */ ){ int i; ShellState *p = (ShellState*)pArg; switch( p->mode ){ case MODE_Line: { int w = 5; if( azArg==0 ) break; for(i=0; i<nArg; i++){ int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator); for(i=0; i<nArg; i++){ fprintf(p->out,"%*s = %s%s", w, azCol[i], azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); } break; } case MODE_Explain: case MODE_Column: { if( p->cnt++==0 ){ for(i=0; i<nArg; i++){ int w, n; if( i<ArraySize(p->colWidth) ){ w = p->colWidth[i]; }else{ w = 0; } if( w==0 ){ w = strlen30(azCol[i] ? azCol[i] : ""); if( w<10 ) w = 10; n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullValue); if( w<n ) w = n; } if( i<ArraySize(p->actualWidth) ){ p->actualWidth[i] = w; } if( p->showHeader ){ if( w<0 ){ fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? p->rowSeparator : " "); }else{ fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? p->rowSeparator : " "); } } } if( p->showHeader ){ for(i=0; i<nArg; i++){ int w; if( i<ArraySize(p->actualWidth) ){ w = p->actualWidth[i]; if( w<0 ) w = -w; }else{ w = 10; } fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" "----------------------------------------------------------", i==nArg-1 ? p->rowSeparator : " "); } } } if( azArg==0 ) break; for(i=0; i<nArg; i++){ int w; if( i<ArraySize(p->actualWidth) ){ w = p->actualWidth[i]; }else{ w = 10; } if( p->mode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){ w = strlen30(azArg[i]); } if( i==1 && p->aiIndent && p->pStmt ){ if( p->iIndent<p->nIndent ){ fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); } p->iIndent++; } if( w<0 ){ fprintf(p->out,"%*.*s%s",-w,-w, azArg[i] ? azArg[i] : p->nullValue, i==nArg-1 ? p->rowSeparator : " "); }else{ fprintf(p->out,"%-*.*s%s",w,w, azArg[i] ? azArg[i] : p->nullValue, i==nArg-1 ? p->rowSeparator : " "); } } break; } case MODE_Semi: case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? p->rowSeparator : p->colSeparator); } } if( azArg==0 ) break; for(i=0; i<nArg; i++){ char *z = azArg[i]; if( z==0 ) z = p->nullValue; fprintf(p->out, "%s", z); if( i<nArg-1 ){ fprintf(p->out, "%s", p->colSeparator); }else if( p->mode==MODE_Semi ){ fprintf(p->out, ";%s", p->rowSeparator); }else{ fprintf(p->out, "%s", p->rowSeparator); } } break; } case MODE_Html: { if( p->cnt++==0 && p->showHeader ){ fprintf(p->out,"<TR>"); for(i=0; i<nArg; i++){ fprintf(p->out,"<TH>"); output_html_string(p->out, azCol[i]); fprintf(p->out,"</TH>\n"); } fprintf(p->out,"</TR>\n"); } if( azArg==0 ) break; fprintf(p->out,"<TR>"); for(i=0; i<nArg; i++){ fprintf(p->out,"<TD>"); output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); fprintf(p->out,"</TD>\n"); } fprintf(p->out,"</TR>\n"); break; } case MODE_Tcl: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ output_c_string(p->out,azCol[i] ? azCol[i] : ""); if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator); } fprintf(p->out, "%s", p->rowSeparator); } if( azArg==0 ) break; for(i=0; i<nArg; i++){ output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator); } fprintf(p->out, "%s", p->rowSeparator); break; } case MODE_Csv: { setBinaryMode(p->out); if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); } fprintf(p->out, "%s", p->rowSeparator); } if( nArg>0 ){ for(i=0; i<nArg; i++){ output_csv(p, azArg[i], i<nArg-1); } fprintf(p->out, "%s", p->rowSeparator); } setTextMode(p->out); break; } case MODE_Insert: { p->cnt++; if( azArg==0 ) break; fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable); for(i=0; i<nArg; i++){ char *zSep = i>0 ? ",": ""; if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ fprintf(p->out,"%sNULL",zSep); }else if( aiType && aiType[i]==SQLITE_TEXT ){ if( zSep[0] ) fprintf(p->out,"%s",zSep); output_quoted_string(p->out, azArg[i]); }else if( aiType && (aiType[i]==SQLITE_INTEGER || aiType[i]==SQLITE_FLOAT) ){ fprintf(p->out,"%s%s",zSep, azArg[i]); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); if( zSep[0] ) fprintf(p->out,"%s",zSep); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ fprintf(p->out,"%s%s",zSep, azArg[i]); }else{ if( zSep[0] ) fprintf(p->out,"%s",zSep); output_quoted_string(p->out, azArg[i]); } } fprintf(p->out,");\n"); break; } case MODE_Ascii: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ if( i>0 ) fprintf(p->out, "%s", p->colSeparator); fprintf(p->out,"%s",azCol[i] ? azCol[i] : ""); } fprintf(p->out, "%s", p->rowSeparator); } if( azArg==0 ) break; for(i=0; i<nArg; i++){ if( i>0 ) fprintf(p->out, "%s", p->colSeparator); fprintf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); } fprintf(p->out, "%s", p->rowSeparator); break; } } return 0; } /* ** This is the callback routine that the SQLite library ** invokes for each row of a query result. */ static int callback(void *pArg, int nArg, char **azArg, char **azCol){ /* since we don't have type info, call the shell_callback with a NULL value */ return shell_callback(pArg, nArg, azArg, azCol, NULL); } /* ** Set the destination table field of the ShellState structure to ** the name of the table given. Escape any quote characters in the ** table name. */ static void set_table_name(ShellState *p, const char *zName){ int i, n; int needQuote; char *z; if( p->zDestTable ){ free(p->zDestTable); p->zDestTable = 0; |
︙ | ︙ | |||
958 959 960 961 962 963 964 | ** ** If the number of columns is 1 and that column contains text "--" ** then write the semicolon on a separate line. That way, if a ** "--" comment occurs at the end of the statement, the comment ** won't consume the semicolon terminator. */ static int run_table_dump_query( | | | | | 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 | ** ** If the number of columns is 1 and that column contains text "--" ** then write the semicolon on a separate line. That way, if a ** "--" comment occurs at the end of the statement, the comment ** won't consume the semicolon terminator. */ static int run_table_dump_query( ShellState *p, /* Query context */ const char *zSelect, /* SELECT statement to extract content */ const char *zFirstRow /* Print before first row, if not NULL */ ){ sqlite3_stmt *pSelect; int rc; int nResult; int i; const char *z; rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; return rc; } rc = sqlite3_step(pSelect); nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ if( zFirstRow ){ fprintf(p->out, "%s", zFirstRow); |
︙ | ︙ | |||
997 998 999 1000 1001 1002 1003 | fprintf(p->out, ";\n"); } rc = sqlite3_step(pSelect); } rc = sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); | | | 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 | fprintf(p->out, ";\n"); } rc = sqlite3_step(pSelect); } rc = sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; } return rc; } /* ** Allocate space and save off current error string. */ |
︙ | ︙ | |||
1021 1022 1023 1024 1025 1026 1027 | } /* ** Display memory stats. */ static int display_stats( sqlite3 *db, /* Database to query */ | | > | > | < < > > | | > | < > > > | < < > > | | | < > > > | > | > | > | > | > > | | > | > | > | | > | | > | > | > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 | } /* ** Display memory stats. */ static int display_stats( sqlite3 *db, /* Database to query */ ShellState *pArg, /* Pointer to ShellState */ int bReset /* True to reset the stats */ ){ int iCur; int iHiwtr; if( pArg && pArg->out ){ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Memory Used: %d (max %d) bytes\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Number of Outstanding Allocations: %d (max %d)\n", iCur, iHiwtr); if( pArg->shellFlgs & SHFLG_Pagecache ){ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Number of Pcache Pages Used: %d (max %d) pages\n", iCur, iHiwtr); } iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Number of Pcache Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); if( pArg->shellFlgs & SHFLG_Scratch ){ iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Number of Scratch Allocations Used: %d (max %d)\n", iCur, iHiwtr); } iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Number of Scratch Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Largest Allocation: %d bytes\n", iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Largest Pcache Allocation: %d bytes\n", iHiwtr); iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Largest Scratch Allocation: %d bytes\n", iHiwtr); #ifdef YYTRACKMAXSTACKDEPTH iHiwtr = iCur = -1; sqlite3_status(SQLITE_STATUS_PARSER_STACK, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Deepest Parser Stack: %d (max %d)\n", iCur, iHiwtr); #endif } if( pArg && pArg->out && db ){ if( pArg->shellFlgs & SHFLG_Lookaside ){ iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Successful lookaside attempts: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Lookaside failures due to size: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr); } iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Pager Heap Usage: %d bytes\n",iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); fprintf(pArg->out, "Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); fprintf(pArg->out, "Page cache misses: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); fprintf(pArg->out, "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Schema Heap Usage: %d bytes\n",iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n",iCur); } if( pArg && pArg->out && db && pArg->pStmt ){ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); fprintf(pArg->out, "Fullscan Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); fprintf(pArg->out, "Sort Operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); } return 0; } /* ** Display scan stats. */ static void display_scanstats( sqlite3 *db, /* Database to query */ ShellState *pArg /* Pointer to ShellState */ ){ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS int i, k, n, mx; fprintf(pArg->out, "-------- scanstats --------\n"); mx = 0; for(k=0; k<=mx; k++){ double rEstLoop = 1.0; for(i=n=0; 1; i++){ sqlite3_stmt *p = pArg->pStmt; sqlite3_int64 nLoop, nVisit; double rEst; int iSid; const char *zExplain; if( sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop) ){ break; } sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_SELECTID, (void*)&iSid); if( iSid>mx ) mx = iSid; if( iSid!=k ) continue; if( n==0 ){ rEstLoop = (double)nLoop; if( k>0 ) fprintf(pArg->out, "-------- subquery %d -------\n", k); } n++; sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst); sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); fprintf(pArg->out, "Loop %2d: %s\n", n, zExplain); rEstLoop *= rEst; fprintf(pArg->out, " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n", nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst ); } } fprintf(pArg->out, "---------------------------\n"); #endif } /* ** Parameter azArray points to a zero-terminated array of strings. zStr ** points to a single nul-terminated string. Return non-zero if zStr ** is equal, according to strcmp(), to any of the strings in the array. ** Otherwise, return zero. */ static int str_in_array(const char *zStr, const char **azArray){ int i; for(i=0; azArray[i]; i++){ if( 0==strcmp(zStr, azArray[i]) ) return 1; } return 0; } /* ** If compiled statement pSql appears to be an EXPLAIN statement, allocate ** and populate the ShellState.aiIndent[] array with the number of ** spaces each opcode should be indented before it is output. ** ** The indenting rules are: ** ** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent ** all opcodes that occur between the p2 jump destination and the opcode ** itself by 2 spaces. ** ** * For each "Goto", if the jump destination is earlier in the program ** and ends on one of: ** Yield SeekGt SeekLt RowSetRead Rewind ** or if the P1 parameter is one instead of zero, ** then indent all opcodes between the earlier instruction ** and "Goto" by 2 spaces. */ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ const char *zSql; /* The text of the SQL statement */ const char *z; /* Used to check if this is an EXPLAIN */ int *abYield = 0; /* True if op is an OP_Yield */ int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */ int iOp; /* Index of operation in p->aiIndent[] */ const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", "NextIfOpen", "PrevIfOpen", 0 }; const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", "Rewind", 0 }; const char *azGoto[] = { "Goto", 0 }; /* Try to figure out if this is really an EXPLAIN statement. If this ** cannot be verified, return early. */ zSql = sqlite3_sql(pSql); if( zSql==0 ) return; for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++); if( sqlite3_strnicmp(z, "explain", 7) ) return; for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){ int i; int iAddr = sqlite3_column_int(pSql, 0); const char *zOp = (const char*)sqlite3_column_text(pSql, 1); /* Set p2 to the P2 field of the current opcode. Then, assuming that ** p2 is an instruction address, set variable p2op to the index of that ** instruction in the aiIndent[] array. p2 and p2op may be different if ** the current instruction is part of a sub-program generated by an ** SQL trigger or foreign key. */ int p2 = sqlite3_column_int(pSql, 3); int p2op = (p2 + (iOp-iAddr)); /* Grow the p->aiIndent array as required */ if( iOp>=nAlloc ){ nAlloc += 100; p->aiIndent = (int*)sqlite3_realloc(p->aiIndent, nAlloc*sizeof(int)); abYield = (int*)sqlite3_realloc(abYield, nAlloc*sizeof(int)); } abYield[iOp] = str_in_array(zOp, azYield); p->aiIndent[iOp] = 0; p->nIndent = iOp+1; if( str_in_array(zOp, azNext) ){ for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2; } if( str_in_array(zOp, azGoto) && p2op<p->nIndent && (abYield[p2op] || sqlite3_column_int(pSql, 2)) ){ for(i=p2op+1; i<iOp; i++) p->aiIndent[i] += 2; } } p->iIndent = 0; sqlite3_free(abYield); sqlite3_reset(pSql); } /* ** Free the array allocated by explain_data_prepare(). */ static void explain_data_delete(ShellState *p){ sqlite3_free(p->aiIndent); p->aiIndent = 0; p->nIndent = 0; p->iIndent = 0; } /* ** Execute a statement or set of statements. Print ** any result rows/columns depending on the current mode ** set via the supplied callback. ** ** This is very similar to SQLite's built-in sqlite3_exec() ** function except it takes a slightly different callback ** and callback data argument. */ static int shell_exec( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ int (*xCallback)(void*,int,char**,char**,int*), /* Callback function */ /* (not the same as sqlite3_exec) */ ShellState *pArg, /* Pointer to ShellState */ char **pzErrMsg /* Error msg written here */ ){ sqlite3_stmt *pStmt = NULL; /* Statement to execute. */ int rc = SQLITE_OK; /* Return Code */ int rc2; const char *zLeftover; /* Tail of unprocessed SQL */ if( pzErrMsg ){ |
︙ | ︙ | |||
1164 1165 1166 1167 1168 1169 1170 | /* echo the sql statement if echo on */ if( pArg && pArg->echoOn ){ const char *zStmtSql = sqlite3_sql(pStmt); fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql); } | | | | > | > > | > | > > | > > > > > > > > > | 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 | /* echo the sql statement if echo on */ if( pArg && pArg->echoOn ){ const char *zStmtSql = sqlite3_sql(pStmt); fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql); } /* Show the EXPLAIN QUERY PLAN if .eqp is on */ if( pArg && pArg->autoEQP ){ sqlite3_stmt *pExplain; char *zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", sqlite3_sql(pStmt)); rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); if( rc==SQLITE_OK ){ while( sqlite3_step(pExplain)==SQLITE_ROW ){ fprintf(pArg->out,"--EQP-- %d,", sqlite3_column_int(pExplain, 0)); fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 1)); fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2)); fprintf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3)); } } sqlite3_finalize(pExplain); sqlite3_free(zEQP); } /* If the shell is currently in ".explain" mode, gather the extra ** data required to add indents to the output.*/ if( pArg && pArg->mode==MODE_Explain ){ explain_data_prepare(pArg, pStmt); } /* perform the first step. this will tell us if we ** have a result set or not and how wide it is. */ rc = sqlite3_step(pStmt); /* if we have a result set... */ |
︙ | ︙ | |||
1200 1201 1202 1203 1204 1205 1206 | for(i=0; i<nCol; i++){ azCols[i] = (char *)sqlite3_column_name(pStmt, i); } do{ /* extract the data and data types */ for(i=0; i<nCol; i++){ aiTypes[i] = x = sqlite3_column_type(pStmt, i); | | | 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 | for(i=0; i<nCol; i++){ azCols[i] = (char *)sqlite3_column_name(pStmt, i); } do{ /* extract the data and data types */ for(i=0; i<nCol; i++){ aiTypes[i] = x = sqlite3_column_type(pStmt, i); if( x==SQLITE_BLOB && pArg && pArg->mode==MODE_Insert ){ azVals[i] = ""; }else{ azVals[i] = (char*)sqlite3_column_text(pStmt, i); } if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){ rc = SQLITE_NOMEM; break; /* from for */ |
︙ | ︙ | |||
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 | } }else{ do{ rc = sqlite3_step(pStmt); } while( rc == SQLITE_ROW ); } } /* print usage stats if stats on */ if( pArg && pArg->statsOn ){ display_stats(db, pArg, 0); } /* Finalize the statement just executed. If this fails, save a ** copy of the error message. Otherwise, set zSql to point to the ** next statement to execute. */ rc2 = sqlite3_finalize(pStmt); if( rc!=SQLITE_NOMEM ) rc = rc2; if( rc==SQLITE_OK ){ | > > > > > > > | 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 | } }else{ do{ rc = sqlite3_step(pStmt); } while( rc == SQLITE_ROW ); } } explain_data_delete(pArg); /* print usage stats if stats on */ if( pArg && pArg->statsOn ){ display_stats(db, pArg, 0); } /* print loop-counters if required */ if( pArg && pArg->scanstatsOn ){ display_scanstats(db, pArg); } /* Finalize the statement just executed. If this fails, save a ** copy of the error message. Otherwise, set zSql to point to the ** next statement to execute. */ rc2 = sqlite3_finalize(pStmt); if( rc!=SQLITE_NOMEM ) rc = rc2; if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
1270 1271 1272 1273 1274 1275 1276 | */ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ int rc; const char *zTable; const char *zType; const char *zSql; const char *zPrepStmt = 0; | | | 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 | */ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ int rc; const char *zTable; const char *zType; const char *zSql; const char *zPrepStmt = 0; ShellState *p = (ShellState *)pArg; UNUSED_PARAMETER(azCol); if( nArg!=3 ) return 1; zTable = azArg[0]; zType = azArg[1]; zSql = azArg[2]; |
︙ | ︙ | |||
1312 1313 1314 1315 1316 1317 1318 | char *zTmp = 0; int nRow = 0; zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0); zTableInfo = appendText(zTableInfo, zTable, '"'); zTableInfo = appendText(zTableInfo, ");", 0); | | | 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 | char *zTmp = 0; int nRow = 0; zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0); zTableInfo = appendText(zTableInfo, zTable, '"'); zTableInfo = appendText(zTableInfo, ");", 0); rc = sqlite3_prepare_v2(p->db, zTableInfo, -1, &pTableInfo, 0); free(zTableInfo); if( rc!=SQLITE_OK || !pTableInfo ){ return 1; } zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0); /* Always quote the table name, even if it appears to be pure ascii, |
︙ | ︙ | |||
1366 1367 1368 1369 1370 1371 1372 | ** Run zQuery. Use dump_callback() as the callback routine so that ** the contents of the query are output as SQL statements. ** ** If we get a SQLITE_CORRUPT error, rerun the query after appending ** "ORDER BY rowid DESC" to the end. */ static int run_schema_dump_query( | | | 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 | ** Run zQuery. Use dump_callback() as the callback routine so that ** the contents of the query are output as SQL statements. ** ** If we get a SQLITE_CORRUPT error, rerun the query after appending ** "ORDER BY rowid DESC" to the end. */ static int run_schema_dump_query( ShellState *p, const char *zQuery ){ int rc; char *zErr = 0; rc = sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr); if( rc==SQLITE_CORRUPT ){ char *zQ2; |
︙ | ︙ | |||
1401 1402 1403 1404 1405 1406 1407 | } /* ** Text of a help message */ static char zHelp[] = ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" | | > > | > | > | | | > | | > | > > > | > | > > > > > > > > > > | < > > > > > > > > | > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > | > > > > > > | 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 | } /* ** Text of a help message */ static char zHelp[] = ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" ".bail on|off Stop after hitting an error. Default OFF\n" ".clone NEWDB Clone data into NEWDB from the existing database\n" ".databases List names and files of attached databases\n" ".dbinfo ?DB? Show status information about the database\n" ".dump ?TABLE? ... Dump the database in an SQL text format\n" " If TABLE specified, only dump tables matching\n" " LIKE pattern TABLE.\n" ".echo on|off Turn command echo on or off\n" ".eqp on|off Enable or disable automatic EXPLAIN QUERY PLAN\n" ".exit Exit this program\n" ".explain ?on|off? Turn output mode suitable for EXPLAIN on or off.\n" " With no args, it turns EXPLAIN on.\n" ".fullschema Show schema and the content of sqlite_stat tables\n" ".headers on|off Turn display of headers on or off\n" ".help Show this message\n" ".import FILE TABLE Import data from FILE into TABLE\n" ".indexes ?TABLE? Show names of all indexes\n" " If TABLE specified, only show indexes for tables\n" " matching LIKE pattern TABLE.\n" #ifdef SQLITE_ENABLE_IOTRACE ".iotrace FILE Enable I/O diagnostic logging to FILE\n" #endif #ifndef SQLITE_OMIT_LOAD_EXTENSION ".load FILE ?ENTRY? Load an extension library\n" #endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" " ascii Columns/rows delimited by 0x1F and 0x1E\n" " csv Comma-separated values\n" " column Left-aligned columns. (See .width)\n" " html HTML <table> code\n" " insert SQL insert statements for TABLE\n" " line One value per line\n" " list Values delimited by .separator strings\n" " tabs Tab-separated values\n" " tcl TCL list elements\n" ".nullvalue STRING Use STRING in place of NULL values\n" ".once FILENAME Output for the next SQL command only to FILENAME\n" ".open ?FILENAME? Close existing database and reopen FILENAME\n" ".output ?FILENAME? Send output to FILENAME or stdout\n" ".print STRING... Print literal STRING\n" ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" ".save FILE Write in-memory database into FILE\n" ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n" ".schema ?TABLE? Show the CREATE statements\n" " If TABLE specified, only show tables matching\n" " LIKE pattern TABLE.\n" ".separator COL ?ROW? Change the column separator and optionally the row\n" " separator for both the output mode and .import\n" ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" ".show Show the current values for various settings\n" ".stats on|off Turn stats on or off\n" ".system CMD ARGS... Run CMD ARGS... in a system shell\n" ".tables ?TABLE? List names of tables\n" " If TABLE specified, only list tables matching\n" " LIKE pattern TABLE.\n" ".timeout MS Try opening locked tables for MS milliseconds\n" ".timer on|off Turn SQL timer on or off\n" ".trace FILE|off Output each SQL statement as it is run\n" ".vfsname ?AUX? Print the name of the VFS stack\n" ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n" " Negative values right-justify\n" ; /* Forward reference */ static int process_input(ShellState *p, FILE *in); /* ** Implementation of the "readfile(X)" SQL function. The entire content ** of the file named X is read and returned as a BLOB. NULL is returned ** if the file does not exist or is unreadable. */ static void readfileFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zName; FILE *in; long nIn; void *pBuf; zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 ) return; in = fopen(zName, "rb"); if( in==0 ) return; fseek(in, 0, SEEK_END); nIn = ftell(in); rewind(in); pBuf = sqlite3_malloc( nIn ); if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ sqlite3_result_blob(context, pBuf, nIn, sqlite3_free); }else{ sqlite3_free(pBuf); } fclose(in); } /* ** Implementation of the "writefile(X,Y)" SQL function. The argument Y ** is written into file X. The number of bytes written is returned. Or ** NULL is returned if something goes wrong, such as being unable to open ** file X for writing. */ static void writefileFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ FILE *out; const char *z; sqlite3_int64 rc; const char *zFile; zFile = (const char*)sqlite3_value_text(argv[0]); if( zFile==0 ) return; out = fopen(zFile, "wb"); if( out==0 ) return; z = (const char*)sqlite3_value_blob(argv[1]); if( z==0 ){ rc = 0; }else{ rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out); } fclose(out); sqlite3_result_int64(context, rc); } /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. */ static void open_db(ShellState *p, int keepAlive){ if( p->db==0 ){ sqlite3_initialize(); sqlite3_open(p->zDbFilename, &p->db); db = p->db; if( db && sqlite3_errcode(db)==SQLITE_OK ){ sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0, shellstaticFunc, 0, 0); } if( db==0 || SQLITE_OK!=sqlite3_errcode(db) ){ fprintf(stderr,"Error: unable to open database \"%s\": %s\n", p->zDbFilename, sqlite3_errmsg(db)); if( keepAlive ) return; exit(1); } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, readfileFunc, 0, 0); sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, writefileFunc, 0, 0); } } /* ** Do C-language style dequoting. ** ** \t -> tab ** \n -> newline ** \r -> carriage return ** \" -> " ** \NNN -> ascii character NNN in octal ** \\ -> backslash */ static void resolve_backslashes(char *z){ int i, j; char c; while( *z && *z!='\\' ) z++; for(i=j=0; (c = z[i])!=0; i++, j++){ if( c=='\\' ){ c = z[++i]; if( c=='n' ){ c = '\n'; }else if( c=='t' ){ c = '\t'; |
︙ | ︙ | |||
1524 1525 1526 1527 1528 1529 1530 | c = (c<<3) + z[i] - '0'; } } } } z[j] = c; } | | | 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 | c = (c<<3) + z[i] - '0'; } } } } z[j] = c; } if( j<i ) z[j] = 0; } /* ** Return the value of a hexadecimal digit. Return -1 if the input ** is not a hex digit. */ static int hexDigitValue(char c){ |
︙ | ︙ | |||
1641 1642 1643 1644 1645 1646 1647 | } /* ** A routine for handling output from sqlite3_trace(). */ static void sql_trace_callback(void *pArg, const char *z){ FILE *f = (FILE*)pArg; | | > > > > | | | | > | | > | | | > > | | | | | | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 | } /* ** A routine for handling output from sqlite3_trace(). */ static void sql_trace_callback(void *pArg, const char *z){ FILE *f = (FILE*)pArg; if( f ){ int i = (int)strlen(z); while( i>0 && z[i-1]==';' ){ i--; } fprintf(f, "%.*s;\n", i, z); } } /* ** A no-op routine that runs with the ".breakpoint" doc-command. This is ** a useful spot to set a debugger breakpoint. */ static void test_breakpoint(void){ static int nCall = 0; nCall++; } /* ** An object used to read a CSV and other files for import. */ typedef struct ImportCtx ImportCtx; struct ImportCtx { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ int cTerm; /* Character that terminated the most recent field */ int cColSep; /* The column separator character. (Usually ",") */ int cRowSep; /* The row separator character. (Usually "\n") */ }; /* Append a single byte to z[] */ static void import_append_char(ImportCtx *p, int c){ if( p->n+1>=p->nAlloc ){ p->nAlloc += p->nAlloc + 100; p->z = sqlite3_realloc(p->z, p->nAlloc); if( p->z==0 ){ fprintf(stderr, "out of memory\n"); exit(1); } } p->z[p->n++] = (char)c; } /* Read a single field of CSV text. Compatible with rfc4180 and extended ** with the option of having a separator other than ",". ** ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc(). ** + Use p->cSep as the column separator. The default is ",". ** + Use p->rSep as the row separator. The default is "\n". ** + Keep track of the line number in p->nLine. ** + Store the character that terminates the field in p->cTerm. Store ** EOF on end-of-file. ** + Report syntax errors on stderr */ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ int c; int cSep = p->cColSep; int rSep = p->cRowSep; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ p->cTerm = EOF; return 0; } if( c=='"' ){ int pc, ppc; int startLine = p->nLine; int cQuote = c; pc = ppc = 0; while( 1 ){ c = fgetc(p->in); if( c==rSep ) p->nLine++; if( c==cQuote ){ if( pc==cQuote ){ pc = 0; continue; } } if( (c==cSep && pc==cQuote) || (c==rSep && pc==cQuote) || (c==rSep && pc=='\r' && ppc==cQuote) || (c==EOF && pc==cQuote) ){ do{ p->n--; }while( p->z[p->n]!=cQuote ); p->cTerm = c; break; } if( pc==cQuote && c!='\r' ){ fprintf(stderr, "%s:%d: unescaped %c character\n", p->zFile, p->nLine, cQuote); } if( c==EOF ){ fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", p->zFile, startLine, cQuote); p->cTerm = c; break; } import_append_char(p, c); ppc = pc; pc = c; } }else{ while( c!=EOF && c!=cSep && c!=rSep ){ import_append_char(p, c); c = fgetc(p->in); } if( c==rSep ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } p->cTerm = c; } if( p->z ) p->z[p->n] = 0; return p->z; } /* Read a single field of ASCII delimited text. ** ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc(). ** + Use p->cSep as the column separator. The default is "\x1F". ** + Use p->rSep as the row separator. The default is "\x1E". ** + Keep track of the row number in p->nLine. ** + Store the character that terminates the field in p->cTerm. Store ** EOF on end-of-file. ** + Report syntax errors on stderr */ static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ int c; int cSep = p->cColSep; int rSep = p->cRowSep; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ p->cTerm = EOF; return 0; } while( c!=EOF && c!=cSep && c!=rSep ){ import_append_char(p, c); c = fgetc(p->in); } if( c==rSep ){ p->nLine++; } p->cTerm = c; if( p->z ) p->z[p->n] = 0; return p->z; } /* ** Try to transfer data for table zTable. If an error is seen while ** moving forward, try to go backwards. The backwards movement won't ** work for WITHOUT ROWID tables. */ static void tryToCloneData( ShellState *p, sqlite3 *newDb, const char *zTable ){ sqlite3_stmt *pQuery = 0; sqlite3_stmt *pInsert = 0; char *zQuery = 0; char *zInsert = 0; int rc; int i, j, n; int nTable = (int)strlen(zTable); int k = 0; int cnt = 0; const int spinRate = 10000; zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ fprintf(stderr, "Error %d: %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_data_xfer; } n = sqlite3_column_count(pQuery); zInsert = sqlite3_malloc(200 + nTable + n*3); if( zInsert==0 ){ fprintf(stderr, "out of memory\n"); goto end_data_xfer; } sqlite3_snprintf(200+nTable,zInsert, "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable); i = (int)strlen(zInsert); for(j=1; j<n; j++){ memcpy(zInsert+i, ",?", 2); i += 2; } memcpy(zInsert+i, ");", 3); rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); if( rc ){ fprintf(stderr, "Error %d: %s on [%s]\n", sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zQuery); goto end_data_xfer; } for(k=0; k<2; k++){ while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ for(i=0; i<n; i++){ switch( sqlite3_column_type(pQuery, i) ){ case SQLITE_NULL: { sqlite3_bind_null(pInsert, i+1); break; } case SQLITE_INTEGER: { sqlite3_bind_int64(pInsert, i+1, sqlite3_column_int64(pQuery,i)); break; } case SQLITE_FLOAT: { sqlite3_bind_double(pInsert, i+1, sqlite3_column_double(pQuery,i)); break; } case SQLITE_TEXT: { sqlite3_bind_text(pInsert, i+1, (const char*)sqlite3_column_text(pQuery,i), -1, SQLITE_STATIC); break; } case SQLITE_BLOB: { sqlite3_bind_blob(pInsert, i+1, sqlite3_column_blob(pQuery,i), sqlite3_column_bytes(pQuery,i), SQLITE_STATIC); break; } } } /* End for */ rc = sqlite3_step(pInsert); if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ fprintf(stderr, "Error %d: %s\n", sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb)); } sqlite3_reset(pInsert); cnt++; if( (cnt%spinRate)==0 ){ printf("%c\b", "|/-\\"[(cnt/spinRate)%4]); fflush(stdout); } } /* End while */ if( rc==SQLITE_DONE ) break; sqlite3_finalize(pQuery); sqlite3_free(zQuery); zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;", zTable); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ fprintf(stderr, "Warning: cannot step \"%s\" backwards", zTable); break; } } /* End for(k=0...) */ end_data_xfer: sqlite3_finalize(pQuery); sqlite3_finalize(pInsert); sqlite3_free(zQuery); sqlite3_free(zInsert); } /* ** Try to transfer all rows of the schema that match zWhere. For ** each row, invoke xForEach() on the object defined by that row. ** If an error is encountered while moving forward through the ** sqlite_master table, try again moving backwards. */ static void tryToCloneSchema( ShellState *p, sqlite3 *newDb, const char *zWhere, void (*xForEach)(ShellState*,sqlite3*,const char*) ){ sqlite3_stmt *pQuery = 0; char *zQuery = 0; int rc; const unsigned char *zName; const unsigned char *zSql; char *zErrMsg = 0; zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" " WHERE %s", zWhere); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ fprintf(stderr, "Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_schema_xfer; } while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ zName = sqlite3_column_text(pQuery, 0); zSql = sqlite3_column_text(pQuery, 1); printf("%s... ", zName); fflush(stdout); sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); if( zErrMsg ){ fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); sqlite3_free(zErrMsg); zErrMsg = 0; } if( xForEach ){ xForEach(p, newDb, (const char*)zName); } printf("done\n"); } if( rc!=SQLITE_DONE ){ sqlite3_finalize(pQuery); sqlite3_free(zQuery); zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master" " WHERE %s ORDER BY rowid DESC", zWhere); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ fprintf(stderr, "Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_schema_xfer; } while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ zName = sqlite3_column_text(pQuery, 0); zSql = sqlite3_column_text(pQuery, 1); printf("%s... ", zName); fflush(stdout); sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); if( zErrMsg ){ fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); sqlite3_free(zErrMsg); zErrMsg = 0; } if( xForEach ){ xForEach(p, newDb, (const char*)zName); } printf("done\n"); } } end_schema_xfer: sqlite3_finalize(pQuery); sqlite3_free(zQuery); } /* ** Open a new database file named "zNewDb". Try to recover as much information ** as possible out of the main database (which might be corrupt) and write it ** into zNewDb. */ static void tryToClone(ShellState *p, const char *zNewDb){ int rc; sqlite3 *newDb = 0; if( access(zNewDb,0)==0 ){ fprintf(stderr, "File \"%s\" already exists.\n", zNewDb); return; } rc = sqlite3_open(zNewDb, &newDb); if( rc ){ fprintf(stderr, "Cannot create output database: %s\n", sqlite3_errmsg(newDb)); }else{ sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); tryToCloneSchema(p, newDb, "type!='table'", 0); sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); } sqlite3_close(newDb); } /* ** Change the output file back to stdout */ static void output_reset(ShellState *p){ if( p->outfile[0]=='|' ){ #ifndef SQLITE_OMIT_POPEN pclose(p->out); #endif }else{ output_file_close(p->out); } p->outfile[0] = 0; p->out = stdout; } /* ** Run an SQL command and return the single integer result. */ static int db_int(ShellState *p, const char *zSql){ sqlite3_stmt *pStmt; int res = 0; sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ res = sqlite3_column_int(pStmt,0); } sqlite3_finalize(pStmt); return res; } /* ** Convert a 2-byte or 4-byte big-endian integer into a native integer */ unsigned int get2byteInt(unsigned char *a){ return (a[0]<<8) + a[1]; } unsigned int get4byteInt(unsigned char *a){ return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3]; } /* ** Implementation of the ".info" command. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ static const struct { const char *zName; int ofst; } aField[] = { { "file change counter:", 24 }, { "database page count:", 28 }, { "freelist page count:", 36 }, { "schema cookie:", 40 }, { "schema format:", 44 }, { "default cache size:", 48 }, { "autovacuum top root:", 52 }, { "incremental vacuum:", 64 }, { "text encoding:", 56 }, { "user version:", 60 }, { "application id:", 68 }, { "software version:", 96 }, }; static const struct { const char *zName; const char *zSql; } aQuery[] = { { "number of tables:", "SELECT count(*) FROM %s WHERE type='table'" }, { "number of indexes:", "SELECT count(*) FROM %s WHERE type='index'" }, { "number of triggers:", "SELECT count(*) FROM %s WHERE type='trigger'" }, { "number of views:", "SELECT count(*) FROM %s WHERE type='view'" }, { "schema size:", "SELECT total(length(sql)) FROM %s" }, }; sqlite3_file *pFile; int i; char *zSchemaTab; char *zDb = nArg>=2 ? azArg[1] : "main"; unsigned char aHdr[100]; open_db(p, 0); if( p->db==0 ) return 1; sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_FILE_POINTER, &pFile); if( pFile==0 || pFile->pMethods==0 || pFile->pMethods->xRead==0 ){ return 1; } i = pFile->pMethods->xRead(pFile, aHdr, 100, 0); if( i!=SQLITE_OK ){ fprintf(stderr, "unable to read database header\n"); return 1; } i = get2byteInt(aHdr+16); if( i==1 ) i = 65536; fprintf(p->out, "%-20s %d\n", "database page size:", i); fprintf(p->out, "%-20s %d\n", "write format:", aHdr[18]); fprintf(p->out, "%-20s %d\n", "read format:", aHdr[19]); fprintf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); for(i=0; i<sizeof(aField)/sizeof(aField[0]); i++){ int ofst = aField[i].ofst; unsigned int val = get4byteInt(aHdr + ofst); fprintf(p->out, "%-20s %u", aField[i].zName, val); switch( ofst ){ case 56: { if( val==1 ) fprintf(p->out, " (utf8)"); if( val==2 ) fprintf(p->out, " (utf16le)"); if( val==3 ) fprintf(p->out, " (utf16be)"); } } fprintf(p->out, "\n"); } if( zDb==0 ){ zSchemaTab = sqlite3_mprintf("main.sqlite_master"); }else if( strcmp(zDb,"temp")==0 ){ zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_master"); }else{ zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_master", zDb); } for(i=0; i<sizeof(aQuery)/sizeof(aQuery[0]); i++){ char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); int val = db_int(p, zSql); sqlite3_free(zSql); fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); return 0; } /* ** If an input line begins with "." then invoke this routine to ** process that line. ** ** Return 1 on error, 2 to exit, and 0 otherwise. */ static int do_meta_command(char *zLine, ShellState *p){ int i = 1; int nArg = 0; int n, c; int rc = 0; char *azArg[50]; /* Parse the input line into tokens. |
︙ | ︙ | |||
1794 1795 1796 1797 1798 1799 1800 | } /* Process the input line. */ if( nArg==0 ) return 0; /* no tokens, no error */ n = strlen30(azArg[0]); c = azArg[0][0]; | | > > | 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 | } /* Process the input line. */ if( nArg==0 ) return 0; /* no tokens, no error */ n = strlen30(azArg[0]); c = azArg[0][0]; if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0) || (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0) ){ const char *zDestFile = 0; const char *zDb = 0; sqlite3 *pDest; sqlite3_backup *pBackup; int j; for(j=1; j<nArg; j++){ const char *z = azArg[j]; |
︙ | ︙ | |||
1830 1831 1832 1833 1834 1835 1836 | if( zDb==0 ) zDb = "main"; rc = sqlite3_open(zDestFile, &pDest); if( rc!=SQLITE_OK ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zDestFile); sqlite3_close(pDest); return 1; } | | | > | > > > > > > > > > > > > > | | | > > > > | | > > > > > | 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 | if( zDb==0 ) zDb = "main"; rc = sqlite3_open(zDestFile, &pDest); if( rc!=SQLITE_OK ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zDestFile); sqlite3_close(pDest); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); if( pBackup==0 ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); sqlite3_close(pDest); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){} sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ rc = 0; }else{ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); rc = 1; } sqlite3_close(pDest); }else if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){ if( nArg==2 ){ bail_on_error = booleanValue(azArg[1]); }else{ fprintf(stderr, "Usage: .bail on|off\n"); rc = 1; } }else /* The undocumented ".breakpoint" command causes a call to the no-op ** routine named test_breakpoint(). */ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ test_breakpoint(); }else if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){ if( nArg==2 ){ tryToClone(p, azArg[1]); }else{ fprintf(stderr, "Usage: .clone FILENAME\n"); rc = 1; } }else if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){ ShellState data; char *zErrMsg = 0; open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 1; data.mode = MODE_Column; data.colWidth[0] = 3; data.colWidth[1] = 15; data.colWidth[2] = 58; data.cnt = 0; sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg); if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else if( c=='d' && strncmp(azArg[0], "dbinfo", n)==0 ){ rc = shell_dbinfo_command(p, nArg, azArg); }else if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ open_db(p, 0); /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ if( nArg!=1 && nArg!=2 ){ fprintf(stderr, "Usage: .dump ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } fprintf(p->out, "PRAGMA foreign_keys=OFF;\n"); fprintf(p->out, "BEGIN TRANSACTION;\n"); p->writableSchema = 0; sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); p->nErr = 0; if( nArg==1 ){ run_schema_dump_query(p, |
︙ | ︙ | |||
1927 1928 1929 1930 1931 1932 1933 | p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); fprintf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n"); }else | | > | > > > > > > > > > > > > > | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > | < < < | | | | | > | > > > > > > | | | > > > > > > > > > > | > > > > > > > > | | | | > > > > | | > | > > > > > | | > | | > | | | | | | | | | | | | | | | > > > > > > > > > > | | | | | | | | | | | | | > | | | > > > > | | 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 | p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); fprintf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n"); }else if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){ if( nArg==2 ){ p->echoOn = booleanValue(azArg[1]); }else{ fprintf(stderr, "Usage: .echo on|off\n"); rc = 1; } }else if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){ if( nArg==2 ){ p->autoEQP = booleanValue(azArg[1]); }else{ fprintf(stderr, "Usage: .eqp on|off\n"); rc = 1; } }else if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); rc = 2; }else if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ int val = nArg>=2 ? booleanValue(azArg[1]) : 1; if(val == 1) { if(!p->normalMode.valid) { p->normalMode.valid = 1; p->normalMode.mode = p->mode; p->normalMode.showHeader = p->showHeader; memcpy(p->normalMode.colWidth,p->colWidth,sizeof(p->colWidth)); } /* We could put this code under the !p->explainValid ** condition so that it does not execute if we are already in ** explain mode. However, always executing it allows us an easy ** was to reset to explain mode in case the user previously ** did an .explain followed by a .width, .mode or .header ** command. */ p->mode = MODE_Explain; p->showHeader = 1; memset(p->colWidth,0,sizeof(p->colWidth)); p->colWidth[0] = 4; /* addr */ p->colWidth[1] = 13; /* opcode */ p->colWidth[2] = 4; /* P1 */ p->colWidth[3] = 4; /* P2 */ p->colWidth[4] = 4; /* P3 */ p->colWidth[5] = 13; /* P4 */ p->colWidth[6] = 2; /* P5 */ p->colWidth[7] = 13; /* Comment */ }else if (p->normalMode.valid) { p->normalMode.valid = 0; p->mode = p->normalMode.mode; p->showHeader = p->normalMode.showHeader; memcpy(p->colWidth,p->normalMode.colWidth,sizeof(p->colWidth)); } }else if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ ShellState data; char *zErrMsg = 0; int doStats = 0; if( nArg!=1 ){ fprintf(stderr, "Usage: .fullschema\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.mode = MODE_Semi; rc = sqlite3_exec(p->db, "SELECT sql FROM" " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" " FROM sqlite_master UNION ALL" " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' " "ORDER BY rowid", callback, &data, &zErrMsg ); if( rc==SQLITE_OK ){ sqlite3_stmt *pStmt; rc = sqlite3_prepare_v2(p->db, "SELECT rowid FROM sqlite_master" " WHERE name GLOB 'sqlite_stat[134]'", -1, &pStmt, 0); doStats = sqlite3_step(pStmt)==SQLITE_ROW; sqlite3_finalize(pStmt); } if( doStats==0 ){ fprintf(p->out, "/* No STAT tables available */\n"); }else{ fprintf(p->out, "ANALYZE sqlite_master;\n"); sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'", callback, &data, &zErrMsg); data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(p->db, "SELECT * FROM sqlite_stat1", shell_callback, &data,&zErrMsg); data.zDestTable = "sqlite_stat3"; shell_exec(p->db, "SELECT * FROM sqlite_stat3", shell_callback, &data,&zErrMsg); data.zDestTable = "sqlite_stat4"; shell_exec(p->db, "SELECT * FROM sqlite_stat4", shell_callback, &data, &zErrMsg); fprintf(p->out, "ANALYZE sqlite_master;\n"); } }else if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){ if( nArg==2 ){ p->showHeader = booleanValue(azArg[1]); }else{ fprintf(stderr, "Usage: .headers on|off\n"); rc = 1; } }else if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ fprintf(p->out, "%s", zHelp); }else if( c=='i' && strncmp(azArg[0], "import", n)==0 ){ char *zTable; /* Insert data into this table */ char *zFile; /* Name of file to extra content from */ sqlite3_stmt *pStmt = NULL; /* A statement */ int nCol; /* Number of columns in the table */ int nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int needCommit; /* True to COMMIT or ROLLBACK at end */ int nSep; /* Number of bytes in p->colSeparator[] */ char *zSql; /* An SQL statement */ ImportCtx sCtx; /* Reader context */ char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close file */ if( nArg!=3 ){ fprintf(stderr, "Usage: .import FILE TABLE\n"); goto meta_command_exit; } zFile = azArg[1]; zTable = azArg[2]; seenInterrupt = 0; memset(&sCtx, 0, sizeof(sCtx)); open_db(p, 0); nSep = strlen30(p->colSeparator); if( nSep==0 ){ fprintf(stderr, "Error: non-null column separator required for import\n"); return 1; } if( nSep>1 ){ fprintf(stderr, "Error: multi-character column separators not allowed" " for import\n"); return 1; } nSep = strlen30(p->rowSeparator); if( nSep==0 ){ fprintf(stderr, "Error: non-null row separator required for import\n"); return 1; } if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator, SEP_CrLf)==0 ){ /* When importing CSV (only), if the row separator is set to the ** default output row separator, change it to the default input ** row separator. This avoids having to maintain different input ** and output row separators. */ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); nSep = strlen30(p->rowSeparator); } if( nSep>1 ){ fprintf(stderr, "Error: multi-character row separators not allowed" " for import\n"); return 1; } sCtx.zFile = zFile; sCtx.nLine = 1; if( sCtx.zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN fprintf(stderr, "Error: pipes are not supported in this OS\n"); return 1; #else sCtx.in = popen(sCtx.zFile+1, "r"); sCtx.zFile = "<pipe>"; xCloser = pclose; #endif }else{ sCtx.in = fopen(sCtx.zFile, "rb"); xCloser = fclose; } if( p->mode==MODE_Ascii ){ xRead = ascii_read_one_field; }else{ xRead = csv_read_one_field; } if( sCtx.in==0 ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); return 1; } sCtx.cColSep = p->colSeparator[0]; sCtx.cRowSep = p->rowSeparator[0]; zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); xCloser(sCtx.in); return 1; } nByte = strlen30(zSql); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); char cSep = '('; while( xRead(&sCtx) ){ zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z); cSep = ','; if( sCtx.cTerm!=sCtx.cColSep ) break; } if( cSep=='(' ){ sqlite3_free(zCreate); sqlite3_free(sCtx.z); xCloser(sCtx.in); fprintf(stderr,"%s: empty file\n", sCtx.zFile); return 1; } zCreate = sqlite3_mprintf("%z\n)", zCreate); rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); sqlite3_free(zCreate); if( rc ){ fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, sqlite3_errmsg(db)); sqlite3_free(sCtx.z); xCloser(sCtx.in); return 1; } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); } sqlite3_free(zSql); if( rc ){ if (pStmt) sqlite3_finalize(pStmt); fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); xCloser(sCtx.in); return 1; } nCol = sqlite3_column_count(pStmt); sqlite3_finalize(pStmt); pStmt = 0; if( nCol==0 ) return 0; /* no columns, no error */ zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); xCloser(sCtx.in); return 1; } sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); j = strlen30(zSql); for(i=1; i<nCol; i++){ zSql[j++] = ','; zSql[j++] = '?'; } zSql[j++] = ')'; zSql[j] = 0; rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); if (pStmt) sqlite3_finalize(pStmt); xCloser(sCtx.in); return 1; } needCommit = sqlite3_get_autocommit(db); if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); do{ int startLine = sCtx.nLine; for(i=0; i<nCol; i++){ char *z = xRead(&sCtx); /* ** Did we reach end-of-file before finding any columns? ** If so, stop instead of NULL filling the remaining columns. */ if( z==0 && i==0 ) break; /* ** Did we reach end-of-file OR end-of-line before finding any ** columns in ASCII mode? If so, stop instead of NULL filling ** the remaining columns. */ if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ fprintf(stderr, "%s:%d: expected %d columns but found %d - " "filling the rest with NULL\n", sCtx.zFile, startLine, nCol, i+1); i += 2; while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } } } if( sCtx.cTerm==sCtx.cColSep ){ do{ xRead(&sCtx); i++; }while( sCtx.cTerm==sCtx.cColSep ); fprintf(stderr, "%s:%d: expected %d columns but found %d - " "extras ignored\n", sCtx.zFile, startLine, nCol, i); } if( i>=nCol ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, sqlite3_errmsg(db)); } } }while( sCtx.cTerm!=EOF ); xCloser(sCtx.in); sqlite3_free(sCtx.z); sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); }else if( c=='i' && (strncmp(azArg[0], "indices", n)==0 || strncmp(azArg[0], "indexes", n)==0) ){ ShellState data; char *zErrMsg = 0; open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.mode = MODE_List; if( nArg==1 ){ rc = sqlite3_exec(p->db, "SELECT name FROM sqlite_master " "WHERE type='index' AND name NOT LIKE 'sqlite_%' " "UNION ALL " "SELECT name FROM sqlite_temp_master " "WHERE type='index' " "ORDER BY 1", callback, &data, &zErrMsg ); }else if( nArg==2 ){ zShellStatic = azArg[1]; rc = sqlite3_exec(p->db, "SELECT name FROM sqlite_master " "WHERE type='index' AND tbl_name LIKE shellstatic() " "UNION ALL " "SELECT name FROM sqlite_temp_master " "WHERE type='index' AND tbl_name LIKE shellstatic() " "ORDER BY 1", callback, &data, &zErrMsg ); zShellStatic = 0; }else{ fprintf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n"); rc = 1; } }else #ifdef SQLITE_ENABLE_IOTRACE if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){ SQLITE_API extern void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...); if( iotrace && iotrace!=stdout ) fclose(iotrace); iotrace = 0; if( nArg<2 ){ sqlite3IoTrace = 0; }else if( strcmp(azArg[1], "-")==0 ){ sqlite3IoTrace = iotracePrintf; iotrace = stdout; |
︙ | ︙ | |||
2197 2198 2199 2200 2201 2202 2203 | sqlite3IoTrace = iotracePrintf; } } }else #endif #ifndef SQLITE_OMIT_LOAD_EXTENSION | | > > > > > | | > > > > | | | > | > | < < > | < < | | | | | | | > | | | | > > > > | | | > | < < | | < > > > > > > > > > > > > > > > > > > | < < < > | > > | > > > > > > > > > > | | | | > > > | > > | | > | | | | | | | > > > > > > | | | > > > > | | 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 | sqlite3IoTrace = iotracePrintf; } } }else #endif #ifndef SQLITE_OMIT_LOAD_EXTENSION if( c=='l' && strncmp(azArg[0], "load", n)==0 ){ const char *zFile, *zProc; char *zErrMsg = 0; if( nArg<2 ){ fprintf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); rc = 1; goto meta_command_exit; } zFile = azArg[1]; zProc = nArg>=3 ? azArg[2] : 0; open_db(p, 0); rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg); if( rc!=SQLITE_OK ){ fprintf(stderr, "Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif if( c=='l' && strncmp(azArg[0], "log", n)==0 ){ if( nArg!=2 ){ fprintf(stderr, "Usage: .log FILENAME\n"); rc = 1; }else{ const char *zFile = azArg[1]; output_file_close(p->pLog); p->pLog = output_file_open(zFile); } }else if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){ const char *zMode = nArg>=2 ? azArg[1] : ""; int n2 = (int)strlen(zMode); int c2 = zMode[0]; if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){ p->mode = MODE_Line; }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){ p->mode = MODE_Column; }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){ p->mode = MODE_List; }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ p->mode = MODE_Html; }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, nArg>=3 ? azArg[2] : "table"); }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ p->mode = MODE_Ascii; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); }else { fprintf(stderr,"Error: mode should be one of: " "ascii column csv html insert line list tabs tcl\n"); rc = 1; } }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ fprintf(stderr, "Usage: .nullvalue STRING\n"); rc = 1; } }else if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){ sqlite3 *savedDb = p->db; const char *zSavedFilename = p->zDbFilename; char *zNewFilename = 0; p->db = 0; if( nArg>=2 ){ p->zDbFilename = zNewFilename = sqlite3_mprintf("%s", azArg[1]); } open_db(p, 1); if( p->db!=0 ){ sqlite3_close(savedDb); sqlite3_free(p->zFreeOnClose); p->zFreeOnClose = zNewFilename; }else{ sqlite3_free(zNewFilename); p->db = savedDb; p->zDbFilename = zSavedFilename; } }else if( c=='o' && (strncmp(azArg[0], "output", n)==0 || strncmp(azArg[0], "once", n)==0) ){ const char *zFile = nArg>=2 ? azArg[1] : "stdout"; if( nArg>2 ){ fprintf(stderr, "Usage: .%s FILE\n", azArg[0]); rc = 1; goto meta_command_exit; } if( n>1 && strncmp(azArg[0], "once", n)==0 ){ if( nArg<2 ){ fprintf(stderr, "Usage: .once FILE\n"); rc = 1; goto meta_command_exit; } p->outCount = 2; }else{ p->outCount = 0; } output_reset(p); if( zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN fprintf(stderr,"Error: pipes are not supported in this OS\n"); rc = 1; p->out = stdout; #else p->out = popen(zFile + 1, "w"); if( p->out==0 ){ fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); p->out = stdout; rc = 1; }else{ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } #endif }else{ p->out = output_file_open(zFile); if( p->out==0 ){ if( strcmp(zFile,"off")!=0 ){ fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile); } p->out = stdout; rc = 1; } else { sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } } }else if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i<nArg; i++){ if( i>1 ) fprintf(p->out, " "); fprintf(p->out, "%s", azArg[i]); } fprintf(p->out, "\n"); }else if( c=='p' && strncmp(azArg[0], "prompt", n)==0 ){ if( nArg >= 2) { strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); } if( nArg >= 3) { strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); } }else if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){ rc = 2; }else if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){ FILE *alt; if( nArg!=2 ){ fprintf(stderr, "Usage: .read FILE\n"); rc = 1; goto meta_command_exit; } alt = fopen(azArg[1], "rb"); if( alt==0 ){ fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ rc = process_input(p, alt); fclose(alt); } }else if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){ const char *zSrcFile; const char *zDb; sqlite3 *pSrc; sqlite3_backup *pBackup; int nTimeout = 0; if( nArg==2 ){ zSrcFile = azArg[1]; zDb = "main"; }else if( nArg==3 ){ zSrcFile = azArg[2]; zDb = azArg[1]; }else{ fprintf(stderr, "Usage: .restore ?DB? FILE\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_open(zSrcFile, &pSrc); if( rc!=SQLITE_OK ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); sqlite3_close(pSrc); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); if( pBackup==0 ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_close(pSrc); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK |
︙ | ︙ | |||
2378 2379 2380 2381 2382 2383 2384 | }else{ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; } sqlite3_close(pSrc); }else | > | > > > > > > > > > > | > > | | | 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 | }else{ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; } sqlite3_close(pSrc); }else if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){ if( nArg==2 ){ p->scanstatsOn = booleanValue(azArg[1]); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS fprintf(stderr, "Warning: .scanstats not available in this build.\n"); #endif }else{ fprintf(stderr, "Usage: .scanstats on|off\n"); rc = 1; } }else if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ ShellState data; char *zErrMsg = 0; open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.mode = MODE_Semi; if( nArg==2 ){ int i; for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]); if( strcmp(azArg[1],"sqlite_master")==0 ){ char *new_argv[2], *new_colv[2]; new_argv[0] = "CREATE TABLE sqlite_master (\n" " type text,\n" " name text,\n" |
︙ | ︙ | |||
2429 2430 2431 2432 2433 2434 2435 | " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " "WHERE lower(tbl_name) LIKE shellstatic()" " AND type!='meta' AND sql NOTNULL " "ORDER BY rowid", callback, &data, &zErrMsg); zShellStatic = 0; } | | | > > > > > > > > > > > > > | | > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > | > > > > > | > | | | | | | > > | > | | | | > | > > > > | | | 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 | " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " "WHERE lower(tbl_name) LIKE shellstatic()" " AND type!='meta' AND sql NOTNULL " "ORDER BY rowid", callback, &data, &zErrMsg); zShellStatic = 0; } }else if( nArg==1 ){ rc = sqlite3_exec(p->db, "SELECT sql FROM " " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" " FROM sqlite_master UNION ALL" " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' " "ORDER BY rowid", callback, &data, &zErrMsg ); }else{ fprintf(stderr, "Usage: .schema ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } if( zErrMsg ){ fprintf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ fprintf(stderr,"Error: querying schema information\n"); rc = 1; }else{ rc = 0; } }else #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ extern int sqlite3SelectTrace; sqlite3SelectTrace = integerValue(azArg[1]); }else #endif #ifdef SQLITE_DEBUG /* Undocumented commands for internal testing. Subject to change ** without notice. */ if( c=='s' && n>=10 && strncmp(azArg[0], "selftest-", 9)==0 ){ if( strncmp(azArg[0]+9, "boolean", n-9)==0 ){ int i, v; for(i=1; i<nArg; i++){ v = booleanValue(azArg[i]); fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); } } if( strncmp(azArg[0]+9, "integer", n-9)==0 ){ int i; sqlite3_int64 v; for(i=1; i<nArg; i++){ char zBuf[200]; v = integerValue(azArg[i]); sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); fprintf(p->out, "%s", zBuf); } } }else #endif if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ fprintf(stderr, "Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); } if( nArg>=3 ){ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); } }else if( c=='s' && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) ){ char *zCmd; int i, x; if( nArg<2 ){ fprintf(stderr, "Usage: .system COMMAND\n"); rc = 1; goto meta_command_exit; } zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]); for(i=2; i<nArg; i++){ zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"", zCmd, azArg[i]); } x = system(zCmd); sqlite3_free(zCmd); if( x ) fprintf(stderr, "System command returns %d\n", x); }else if( c=='s' && strncmp(azArg[0], "show", n)==0 ){ int i; if( nArg!=1 ){ fprintf(stderr, "Usage: .show\n"); rc = 1; goto meta_command_exit; } fprintf(p->out,"%12.12s: %s\n","echo", p->echoOn ? "on" : "off"); fprintf(p->out,"%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","explain", p->normalMode.valid ? "on" :"off"); fprintf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off"); fprintf(p->out,"%12.12s: %s\n","mode", modeDescr[p->mode]); fprintf(p->out,"%12.12s: ", "nullvalue"); output_c_string(p->out, p->nullValue); fprintf(p->out, "\n"); fprintf(p->out,"%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); fprintf(p->out,"%12.12s: ", "colseparator"); output_c_string(p->out, p->colSeparator); fprintf(p->out, "\n"); fprintf(p->out,"%12.12s: ", "rowseparator"); output_c_string(p->out, p->rowSeparator); fprintf(p->out, "\n"); fprintf(p->out,"%12.12s: %s\n","stats", p->statsOn ? "on" : "off"); fprintf(p->out,"%12.12s: ","width"); for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { fprintf(p->out,"%d ",p->colWidth[i]); } fprintf(p->out,"\n"); }else if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){ if( nArg==2 ){ p->statsOn = booleanValue(azArg[1]); }else{ fprintf(stderr, "Usage: .stats on|off\n"); rc = 1; } }else if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){ sqlite3_stmt *pStmt; char **azResult; int nRow, nAlloc; char *zSql = 0; int ii; open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ) return rc; zSql = sqlite3_mprintf( "SELECT name FROM sqlite_master" " WHERE type IN ('table','view')" " AND name NOT LIKE 'sqlite_%%'" " AND name LIKE ?1"); |
︙ | ︙ | |||
2581 2582 2583 2584 2585 2586 2587 | } nPrintCol = 80/(maxlen+2); if( nPrintCol<1 ) nPrintCol = 1; nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; for(i=0; i<nPrintRow; i++){ for(j=i; j<nRow; j+=nPrintRow){ char *zSp = j<nPrintRow ? "" : " "; | | | 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 | } nPrintCol = 80/(maxlen+2); if( nPrintCol<1 ) nPrintCol = 1; nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; for(i=0; i<nPrintRow; i++){ for(j=i; j<nRow; j+=nPrintRow){ char *zSp = j<nPrintRow ? "" : " "; fprintf(p->out, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); } fprintf(p->out, "\n"); } } for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]); sqlite3_free(azResult); }else |
︙ | ︙ | |||
2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 | { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE }, { "assert", SQLITE_TESTCTRL_ASSERT }, { "always", SQLITE_TESTCTRL_ALWAYS }, { "reserve", SQLITE_TESTCTRL_RESERVE }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, }; int testctrl = -1; int rc = 0; int i, n; | > > > | | 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 | { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE }, { "assert", SQLITE_TESTCTRL_ASSERT }, { "always", SQLITE_TESTCTRL_ALWAYS }, { "reserve", SQLITE_TESTCTRL_RESERVE }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT }, { "imposter", SQLITE_TESTCTRL_IMPOSTER }, }; int testctrl = -1; int rc = 0; int i, n; open_db(p, 0); /* convert testctrl text option to value. allow any unique prefix ** of the option name, or a numerical value. */ n = strlen30(azArg[1]); for(i=0; i<(int)(sizeof(aCtrl)/sizeof(aCtrl[0])); i++){ if( strncmp(azArg[1], aCtrl[i].zCtrlName, n)==0 ){ if( testctrl<0 ){ |
︙ | ︙ | |||
2648 2649 2650 2651 2652 2653 2654 | } else { fprintf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); } break; /* sqlite3_test_control(int) */ | | | > | 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 | } else { fprintf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); } break; /* sqlite3_test_control(int) */ case SQLITE_TESTCTRL_PRNG_SAVE: case SQLITE_TESTCTRL_PRNG_RESTORE: case SQLITE_TESTCTRL_PRNG_RESET: case SQLITE_TESTCTRL_BYTEORDER: if( nArg==2 ){ rc = sqlite3_test_control(testctrl); fprintf(p->out, "%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes no options\n", azArg[1]); } break; |
︙ | ︙ | |||
2673 2674 2675 2676 2677 2678 2679 | fprintf(stderr,"Error: testctrl %s takes a single unsigned" " int option\n", azArg[1]); } break; /* sqlite3_test_control(int, int) */ case SQLITE_TESTCTRL_ASSERT: | | > | 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 | fprintf(stderr,"Error: testctrl %s takes a single unsigned" " int option\n", azArg[1]); } break; /* sqlite3_test_control(int, int) */ case SQLITE_TESTCTRL_ASSERT: case SQLITE_TESTCTRL_ALWAYS: case SQLITE_TESTCTRL_NEVER_CORRUPT: if( nArg==3 ){ int opt = booleanValue(azArg[2]); rc = sqlite3_test_control(testctrl, opt); fprintf(p->out, "%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); |
︙ | ︙ | |||
2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 | fprintf(p->out, "%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single char * option\n", azArg[1]); } break; #endif case SQLITE_TESTCTRL_BITVEC_TEST: case SQLITE_TESTCTRL_FAULT_INSTALL: case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: case SQLITE_TESTCTRL_SCRATCHMALLOC: default: fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n", azArg[1]); break; } } }else | > > > > > > > > > > > > | | | | | < | > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 | fprintf(p->out, "%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single char * option\n", azArg[1]); } break; #endif case SQLITE_TESTCTRL_IMPOSTER: if( nArg==5 ){ rc = sqlite3_test_control(testctrl, p->db, azArg[2], integerValue(azArg[3]), integerValue(azArg[4])); fprintf(p->out, "%d (0x%08x)\n", rc, rc); }else{ fprintf(stderr,"Usage: .testctrl imposter dbName onoff tnum\n"); } break; case SQLITE_TESTCTRL_BITVEC_TEST: case SQLITE_TESTCTRL_FAULT_INSTALL: case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: case SQLITE_TESTCTRL_SCRATCHMALLOC: default: fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n", azArg[1]); break; } } }else if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 ){ open_db(p, 0); sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0); }else if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 ){ if( nArg==2 ){ enableTimer = booleanValue(azArg[1]); if( enableTimer && !HAS_TIMER ){ fprintf(stderr, "Error: timer not available on this system.\n"); enableTimer = 0; } }else{ fprintf(stderr, "Usage: .timer on|off\n"); rc = 1; } }else if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){ open_db(p, 0); if( nArg!=2 ){ fprintf(stderr, "Usage: .trace FILE|off\n"); rc = 1; goto meta_command_exit; } output_file_close(p->traceOut); p->traceOut = output_file_open(azArg[1]); #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) if( p->traceOut==0 ){ sqlite3_trace(p->db, 0, 0); }else{ sqlite3_trace(p->db, sql_trace_callback, p->traceOut); } #endif }else #if SQLITE_USER_AUTHENTICATION if( c=='u' && strncmp(azArg[0], "user", n)==0 ){ if( nArg<2 ){ fprintf(stderr, "Usage: .user SUBCOMMAND ...\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); if( strcmp(azArg[1],"login")==0 ){ if( nArg!=4 ){ fprintf(stderr, "Usage: .user login USER PASSWORD\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], (int)strlen(azArg[3])); if( rc ){ fprintf(stderr, "Authentication failed for user %s\n", azArg[2]); rc = 1; } }else if( strcmp(azArg[1],"add")==0 ){ if( nArg!=5 ){ fprintf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_add(p->db, azArg[2], azArg[3], (int)strlen(azArg[3]), booleanValue(azArg[4])); if( rc ){ fprintf(stderr, "User-Add failed: %d\n", rc); rc = 1; } }else if( strcmp(azArg[1],"edit")==0 ){ if( nArg!=5 ){ fprintf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_change(p->db, azArg[2], azArg[3], (int)strlen(azArg[3]), booleanValue(azArg[4])); if( rc ){ fprintf(stderr, "User-Edit failed: %d\n", rc); rc = 1; } }else if( strcmp(azArg[1],"delete")==0 ){ if( nArg!=3 ){ fprintf(stderr, "Usage: .user delete USER\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_delete(p->db, azArg[2]); if( rc ){ fprintf(stderr, "User-Delete failed: %d\n", rc); rc = 1; } }else{ fprintf(stderr, "Usage: .user login|add|edit|delete ...\n"); rc = 1; goto meta_command_exit; } }else #endif /* SQLITE_USER_AUTHENTICATION */ if( c=='v' && strncmp(azArg[0], "version", n)==0 ){ fprintf(p->out, "SQLite %s %s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid()); }else if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){ const char *zDbName = nArg==2 ? azArg[1] : "main"; char *zVfsName = 0; if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); if( zVfsName ){ fprintf(p->out, "%s\n", zVfsName); sqlite3_free(zVfsName); } } }else #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){ extern int sqlite3WhereTrace; sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff; }else #endif if( c=='w' && strncmp(azArg[0], "width", n)==0 ){ int j; assert( nArg<=ArraySize(azArg) ); for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){ p->colWidth[j-1] = (int)integerValue(azArg[j]); } }else { fprintf(stderr, "Error: unknown command or invalid arguments: " " \"%s\". Enter \".help\" for help\n", azArg[0]); rc = 1; } meta_command_exit: if( p->outCount ){ p->outCount--; if( p->outCount==0 ) output_reset(p); } return rc; } /* ** Return TRUE if a semicolon occurs anywhere in the first N characters ** of string z[]. */ |
︙ | ︙ | |||
2849 2850 2851 2852 2853 2854 2855 | ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. ** ** Return the number of errors. */ | | | 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 | ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. ** ** Return the number of errors. */ static int process_input(ShellState *p, FILE *in){ char *zLine = 0; /* A single input line */ char *zSql = 0; /* Accumulated SQL text */ int nLine; /* Length of current line */ int nSql = 0; /* Bytes of zSql[] used */ int nAlloc = 0; /* Allocated zSql[] space */ int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */ char *zErrMsg; /* Error message returned */ |
︙ | ︙ | |||
2875 2876 2877 2878 2879 2880 2881 | break; } if( seenInterrupt ){ if( in!=0 ) break; seenInterrupt = 0; } lineno++; | | > > > | 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 | break; } if( seenInterrupt ){ if( in!=0 ) break; seenInterrupt = 0; } lineno++; if( nSql==0 && _all_whitespace(zLine) ){ if( p->echoOn ) printf("%s\n", zLine); continue; } if( zLine && zLine[0]=='.' && nSql==0 ){ if( p->echoOn ) printf("%s\n", zLine); rc = do_meta_command(zLine, p); if( rc==2 ){ /* exit requested */ break; }else if( rc ){ errCnt++; |
︙ | ︙ | |||
2914 2915 2916 2917 2918 2919 2920 | zSql[nSql++] = '\n'; memcpy(zSql+nSql, zLine, nLine+1); nSql += nLine; } if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) && sqlite3_complete(zSql) ){ p->cnt = 0; | | | 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 | zSql[nSql++] = '\n'; memcpy(zSql+nSql, zLine, nLine+1); nSql += nLine; } if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) && sqlite3_complete(zSql) ){ p->cnt = 0; open_db(p, 0); BEGIN_TIMER; rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg); END_TIMER; if( rc || zErrMsg ){ char zPrefix[100]; if( in!=0 || !stdin_is_interactive ){ sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
︙ | ︙ | |||
2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 | zErrMsg = 0; }else{ fprintf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db)); } errCnt++; } nSql = 0; }else if( nSql && _all_whitespace(zSql) ){ nSql = 0; } } if( nSql ){ if( !_all_whitespace(zSql) ){ fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); } free(zSql); } free(zLine); return errCnt>0; } /* ** Return a pathname which is the user's home directory. A ** 0 return indicates an error of some kind. */ static char *find_home_dir(void){ static char *home_dir = NULL; if( home_dir ) return home_dir; | > > > > > > | > | 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 | zErrMsg = 0; }else{ fprintf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db)); } errCnt++; } nSql = 0; if( p->outCount ){ output_reset(p); p->outCount = 0; } }else if( nSql && _all_whitespace(zSql) ){ if( p->echoOn ) printf("%s\n", zSql); nSql = 0; } } if( nSql ){ if( !_all_whitespace(zSql) ){ fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); errCnt++; } free(zSql); } free(zLine); return errCnt>0; } /* ** Return a pathname which is the user's home directory. A ** 0 return indicates an error of some kind. */ static char *find_home_dir(void){ static char *home_dir = NULL; if( home_dir ) return home_dir; #if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) \ && !defined(__RTP__) && !defined(_WRS_KERNEL) { struct passwd *pwent; uid_t uid = getuid(); if( (pwent=getpwuid(uid)) != NULL) { home_dir = pwent->pw_dir; } } |
︙ | ︙ | |||
3019 3020 3021 3022 3023 3024 3025 | /* ** Read input from the file given by sqliterc_override. Or if that ** parameter is NULL, take input from ~/.sqliterc ** ** Returns the number of errors. */ | | | < < | < > | | < > > > > > | | 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 | /* ** Read input from the file given by sqliterc_override. Or if that ** parameter is NULL, take input from ~/.sqliterc ** ** Returns the number of errors. */ static void process_sqliterc( ShellState *p, /* Configuration data */ const char *sqliterc_override /* Name of config file. NULL to use default */ ){ char *home_dir = NULL; const char *sqliterc = sqliterc_override; char *zBuf = 0; FILE *in = NULL; if (sqliterc == NULL) { home_dir = find_home_dir(); if( home_dir==0 ){ fprintf(stderr, "-- warning: cannot find home directory;" " cannot read ~/.sqliterc\n"); return; } sqlite3_initialize(); zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir); sqliterc = zBuf; } in = fopen(sqliterc,"rb"); if( in ){ if( stdin_is_interactive ){ fprintf(stderr,"-- Loading resources from %s\n",sqliterc); } process_input(p,in); fclose(in); } sqlite3_free(zBuf); } /* ** Show available command line options */ static const char zOptions[] = " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" " -echo print commands before execution\n" " -init FILENAME read/process named file\n" " -[no]header turn headers on or off\n" #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) " -heap SIZE Size of heap for memsys3 or memsys5\n" #endif " -help show this message\n" " -html set output mode to HTML\n" " -interactive force interactive I/O\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -scratch SIZE N use N slots of SZ bytes each for scratch memory\n" " -separator SEP set output column separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" #endif ; |
︙ | ︙ | |||
3102 3103 3104 3105 3106 3107 3108 | } exit(1); } /* ** Initialize the state information in data */ | | > | > > > | > > > > > > > > > > > > > > > > > > | | < > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | < | < < < < > | 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 | } exit(1); } /* ** Initialize the state information in data */ static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); data->mode = MODE_List; memcpy(data->colSeparator,SEP_Column, 2); memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); } /* ** Output text to the console in a font that attracts extra attention. */ #ifdef _WIN32 static void printBold(const char *zText){ HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo; GetConsoleScreenBufferInfo(out, &defaultScreenInfo); SetConsoleTextAttribute(out, FOREGROUND_RED|FOREGROUND_INTENSITY ); printf("%s", zText); SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); } #else static void printBold(const char *zText){ printf("\033[1m%s\033[0m", zText); } #endif /* ** Get the argument to an --option. Throw an error and die if no argument ** is available. */ static char *cmdline_option_value(int argc, char **argv, int i){ if( i==argc ){ fprintf(stderr, "%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); exit(1); } return argv[i]; } int SQLITE_CDECL main(int argc, char **argv){ char *zErrMsg = 0; ShellState data; const char *zInitFile = 0; int i; int rc = 0; int warnInmemoryDb = 0; int readStdin = 1; int nCmd = 0; char **azCmd = 0; #if USE_SYSTEM_SQLITE+0!=1 if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){ fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", sqlite3_sourceid(), SQLITE_SOURCE_ID); exit(1); } #endif setBinaryMode(stdin); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ Argv0 = argv[0]; main_init(&data); stdin_is_interactive = isatty(0); /* Make sure we have a valid signal handler early, before anything ** else is done. */ #ifdef SIGINT signal(SIGINT, interrupt_handler); #endif #ifdef SQLITE_SHELL_DBNAME_PROC { /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name ** of a C-function that will provide the name of the database file. Use ** this compile-time option to embed this shell program in larger ** applications. */ extern void SQLITE_SHELL_DBNAME_PROC(const char**); SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename); warnInmemoryDb = 0; } #endif /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** the size of the alternative malloc heap, ** and the first command to execute. */ for(i=1; i<argc; i++){ char *z; z = argv[i]; if( z[0]!='-' ){ if( data.zDbFilename==0 ){ data.zDbFilename = z; }else{ /* Excesss arguments are interpreted as SQL (or dot-commands) and ** mean that nothing is read from stdin */ readStdin = 0; nCmd++; azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd); if( azCmd==0 ){ fprintf(stderr, "out of memory\n"); exit(1); } azCmd[nCmd-1] = z; } } if( z[1]=='-' ) z++; if( strcmp(z,"-separator")==0 || strcmp(z,"-nullvalue")==0 || strcmp(z,"-newline")==0 || strcmp(z,"-cmd")==0 ){ (void)cmdline_option_value(argc, argv, ++i); }else if( strcmp(z,"-init")==0 ){ zInitFile = cmdline_option_value(argc, argv, ++i); }else if( strcmp(z,"-batch")==0 ){ /* Need to check for batch mode here to so we can avoid printing |
︙ | ︙ | |||
3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 | sqlite3_int64 szHeap; zSize = cmdline_option_value(argc, argv, ++i); szHeap = integerValue(zSize); if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #endif #ifdef SQLITE_ENABLE_VFSTRACE }else if( strcmp(z,"-vfstrace")==0 ){ extern int vfstrace_register( const char *zTraceName, const char *zOldVfsName, int (*xOut)(const char*,void*), void *pOutArg, | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 | sqlite3_int64 szHeap; zSize = cmdline_option_value(argc, argv, ++i); szHeap = integerValue(zSize); if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #endif }else if( strcmp(z,"-scratch")==0 ){ int n, sz; sz = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( sz>400000 ) sz = 400000; if( sz<2500 ) sz = 2500; n = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( n>10 ) n = 10; if( n<1 ) n = 1; sqlite3_config(SQLITE_CONFIG_SCRATCH, malloc(n*sz+1), sz, n); data.shellFlgs |= SHFLG_Scratch; }else if( strcmp(z,"-pagecache")==0 ){ int n, sz; sz = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( sz>70000 ) sz = 70000; if( sz<800 ) sz = 800; n = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( n<10 ) n = 10; sqlite3_config(SQLITE_CONFIG_PAGECACHE, malloc(n*sz+1), sz, n); data.shellFlgs |= SHFLG_Pagecache; }else if( strcmp(z,"-lookaside")==0 ){ int n, sz; sz = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( sz<0 ) sz = 0; n = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( n<0 ) n = 0; sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n); if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside; #ifdef SQLITE_ENABLE_VFSTRACE }else if( strcmp(z,"-vfstrace")==0 ){ extern int vfstrace_register( const char *zTraceName, const char *zOldVfsName, int (*xOut)(const char*,void*), void *pOutArg, |
︙ | ︙ | |||
3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 | exit(1); } } } if( data.zDbFilename==0 ){ #ifndef SQLITE_OMIT_MEMORYDB data.zDbFilename = ":memory:"; #else fprintf(stderr,"%s: Error: no database filename specified\n", Argv0); return 1; #endif | > < < < < < < | | < < < | 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 | exit(1); } } } if( data.zDbFilename==0 ){ #ifndef SQLITE_OMIT_MEMORYDB data.zDbFilename = ":memory:"; warnInmemoryDb = argc==1; #else fprintf(stderr,"%s: Error: no database filename specified\n", Argv0); return 1; #endif } data.out = stdout; /* Go ahead and open the database file if it already exists. If the ** file does not exist, delay opening it. This prevents empty database ** files from being created if a user mistypes the database name argument ** to the sqlite command-line tool. */ if( access(data.zDbFilename, 0)==0 ){ open_db(&data, 0); } /* Process the initialization file if there is one. If no -init option ** is given on the command line, look for a file named ~/.sqliterc and ** try to process it. */ process_sqliterc(&data,zInitFile); /* Make a second pass through the command-line argument and set ** options. This second pass is delayed until after the initialization ** file is processed so that the command-line arguments will override ** settings in the initialization file. */ for(i=1; i<argc; i++){ |
︙ | ︙ | |||
3280 3281 3282 3283 3284 3285 3286 | data.mode = MODE_List; }else if( strcmp(z,"-line")==0 ){ data.mode = MODE_Line; }else if( strcmp(z,"-column")==0 ){ data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; | | > > > > > > | > > > | > > > > > > > > > > > > > > | | > > | > | | | | | | | | | | | | | | > > | < > > > > > > < | < | | > | 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 | data.mode = MODE_List; }else if( strcmp(z,"-line")==0 ){ data.mode = MODE_Line; }else if( strcmp(z,"-column")==0 ){ data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.colSeparator,",",2); }else if( strcmp(z,"-ascii")==0 ){ data.mode = MODE_Ascii; sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Unit); sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Record); }else if( strcmp(z,"-separator")==0 ){ sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-newline")==0 ){ sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-nullvalue")==0 ){ sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; }else if( strcmp(z,"-echo")==0 ){ data.echoOn = 1; }else if( strcmp(z,"-eqp")==0 ){ data.autoEQP = 1; }else if( strcmp(z,"-stats")==0 ){ data.statsOn = 1; }else if( strcmp(z,"-scanstats")==0 ){ data.scanstatsOn = 1; }else if( strcmp(z,"-bail")==0 ){ bail_on_error = 1; }else if( strcmp(z,"-version")==0 ){ printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); return 0; }else if( strcmp(z,"-interactive")==0 ){ stdin_is_interactive = 1; }else if( strcmp(z,"-batch")==0 ){ stdin_is_interactive = 0; }else if( strcmp(z,"-heap")==0 ){ i++; }else if( strcmp(z,"-scratch")==0 ){ i+=2; }else if( strcmp(z,"-pagecache")==0 ){ i+=2; }else if( strcmp(z,"-lookaside")==0 ){ i+=2; }else if( strcmp(z,"-mmap")==0 ){ i++; }else if( strcmp(z,"-vfs")==0 ){ i++; #ifdef SQLITE_ENABLE_VFSTRACE }else if( strcmp(z,"-vfstrace")==0 ){ i++; #endif #ifdef SQLITE_ENABLE_MULTIPLEX }else if( strcmp(z,"-multiplex")==0 ){ i++; #endif }else if( strcmp(z,"-help")==0 ){ usage(1); }else if( strcmp(z,"-cmd")==0 ){ /* Run commands that follow -cmd first and separately from commands ** that simply appear on the command-line. This seems goofy. It would ** be better if all commands ran in the order that they appear. But ** we retain the goofy behavior for historical compatibility. */ if( i==argc-1 ) break; z = cmdline_option_value(argc,argv,++i); if( z[0]=='.' ){ rc = do_meta_command(z, &data); if( rc && bail_on_error ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg); if( zErrMsg!=0 ){ fprintf(stderr,"Error: %s\n", zErrMsg); if( bail_on_error ) return rc!=0 ? rc : 1; }else if( rc!=0 ){ fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z); if( bail_on_error ) return rc; } } }else{ fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } } if( !readStdin ){ /* Run all arguments that do not begin with '-' as if they were separate ** command-line inputs, except for the argToSkip argument which contains ** the database filename. */ for(i=0; i<nCmd; i++){ if( azCmd[i][0]=='.' ){ rc = do_meta_command(azCmd[i], &data); if( rc ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); rc = shell_exec(data.db, azCmd[i], shell_callback, &data, &zErrMsg); if( zErrMsg!=0 ){ fprintf(stderr,"Error: %s\n", zErrMsg); return rc!=0 ? rc : 1; }else if( rc!=0 ){ fprintf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); return rc; } } } free(azCmd); }else{ /* Run commands received from standard input */ if( stdin_is_interactive ){ char *zHome; char *zHistory = 0; int nHistory; printf( "SQLite version %s %.19s\n" /*extra-version-info*/ "Enter \".help\" for usage hints.\n", sqlite3_libversion(), sqlite3_sourceid() ); if( warnInmemoryDb ){ printf("Connected to a "); printBold("transient in-memory database"); printf(".\nUse \".open FILENAME\" to reopen on a " "persistent database.\n"); } zHome = find_home_dir(); if( zHome ){ nHistory = strlen30(zHome) + 20; if( (zHistory = malloc(nHistory))!=0 ){ sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); } } if( zHistory ) shell_read_history(zHistory); rc = process_input(&data, 0); if( zHistory ){ shell_stifle_history(100); shell_write_history(zHistory); free(zHistory); } }else{ rc = process_input(&data, stdin); } } set_table_name(&data, 0); if( data.db ){ sqlite3_close(data.db); } sqlite3_free(data.zFreeOnClose); return rc; } |
Changes to src/shun.c.
︙ | ︙ | |||
38 39 40 41 42 43 44 | /* ** WEBPAGE: shun */ void shun_page(void){ Stmt q; int cnt = 0; const char *zUuid = P("uuid"); | > > > | > | | > > > > > | | > > | > > | | > > > > | | > > > > > > > > > > | | > > > > > | | > > > > > > | > | > | | > > > | | > > | | | < < < < < | | | | | | | | | | | > > | > > > > > > > > > > > > > | | | | | | | > > > > > > > > > > > > | | | | > > > | > > > > > > > > > | | | > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | /* ** WEBPAGE: shun */ void shun_page(void){ Stmt q; int cnt = 0; const char *zUuid = P("uuid"); const char *zShun = P("shun"); const char *zAccept = P("accept"); const char *zRcvid = P("rcvid"); int nRcvid = 0; int numRows = 3; char *zCanonical = 0; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } if( P("rebuild") ){ db_close(1); db_open_repository(g.zRepositoryName); db_begin_transaction(); rebuild_db(0, 0, 0); admin_log("Rebuilt database."); db_end_transaction(0); } if( zUuid ){ char *p; int i = 0; int j = 0; zCanonical = fossil_malloc(strlen(zUuid)+2); while( zUuid[i] ){ if( fossil_isspace(zUuid[i]) ){ if( j && zCanonical[j-1] ){ zCanonical[j] = 0; j++; } }else{ zCanonical[j] = zUuid[i]; j++; } i++; } zCanonical[j+1] = zCanonical[j] = 0; p = zCanonical; while( *p ){ int nUuid = strlen(p); if( nUuid!=UUID_SIZE || !validate16(p, nUuid) ){ @ <p class="generalError">Error: Bad artifact IDs.</p> fossil_free(zCanonical); zCanonical = 0; break; }else{ canonical16(p, UUID_SIZE); p += UUID_SIZE+1; } } zUuid = zCanonical; } style_header("Shunned Artifacts"); if( zUuid && P("sub") ){ const char *p = zUuid; int allExist = 1; login_verify_csrf_secret(); while( *p ){ db_multi_exec("DELETE FROM shun WHERE uuid=%Q", p); if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){ allExist = 0; } admin_log("Unshunned %Q", p); p += UUID_SIZE+1; } if( allExist ){ @ <p class="noMoreShun">Artifact(s)<br /> for( p = zUuid ; *p ; p += UUID_SIZE+1 ){ @ <a href="%R/artifact/%s(p)">%s(p)</a><br /> } @ are no longer being shunned.</p> }else{ @ <p class="noMoreShun">Artifact(s)<br /> for( p = zUuid ; *p ; p += UUID_SIZE+1 ){ @ %s(p)<br /> } @ will no longer be shunned. But they may not exist in the repository. @ It may be necessary to rebuild the repository using the @ <b>fossil rebuild</b> command-line before the artifact content @ can pulled in from other repositories.</p> } } if( zUuid && P("add") ){ const char *p = zUuid; int rid, tagid; login_verify_csrf_secret(); while( *p ){ db_multi_exec( "INSERT OR IGNORE INTO shun(uuid,mtime)" " VALUES(%Q, now())", p); db_multi_exec("DELETE FROM attachment WHERE src=%Q", p); rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", p); if( rid ){ db_multi_exec("DELETE FROM event WHERE objid=%d", rid); } tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='tkt-%q'", p); if( tagid ){ db_multi_exec("DELETE FROM ticket WHERE tkt_uuid=%Q", p); db_multi_exec("DELETE FROM tag WHERE tagid=%d", tagid); db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid); } admin_log("Shunned %Q", p); p += UUID_SIZE+1; } @ <p class="shunned">Artifact(s)<br /> for( p = zUuid ; *p ; p += UUID_SIZE+1 ){ @ <a href="%R/artifact/%s(p)">%s(p)</a><br /> } @ have been shunned. They will no longer be pushed. @ They will be removed from the repository the next time the repository @ is rebuilt using the <b>fossil rebuild</b> command-line</p> } if( zRcvid ){ nRcvid = atoi(zRcvid); numRows = db_int(0, "SELECT min(count(), 10) FROM blob WHERE rcvid=%d", nRcvid); } @ <p>A shunned artifact will not be pushed nor accepted in a pull and the @ artifact content will be purged from the repository the next time the @ repository is rebuilt. A list of shunned artifacts can be seen at the @ bottom of this page.</p> @ @ <a name="addshun"></a> @ <p>To shun artifacts, enter their artifact IDs (the 40-character SHA1 @ hash of the artifacts) in the @ following box and press the "Shun" button. This will cause the artifacts @ to be removed from the repository and will prevent the artifacts from being @ readded to the repository by subsequent sync operation.</p> @ @ <p>Note that you must enter the full 40-character artifact IDs, not @ an abbreviation or a symbolic tag.</p> @ @ <p>Warning: Shunning should only be used to remove inappropriate content @ from the repository. Inappropriate content includes such things as @ spam added to Wiki, files that violate copyright or patent agreements, @ or artifacts that by design or accident interfere with the processing @ of the repository. Do not shun artifacts merely to remove them from @ sight - set the "hidden" tag on such artifacts instead.</p> @ @ <blockquote> @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> login_insert_csrf_secret(); @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> if( zShun ){ if( strlen(zShun) ){ @ %h(zShun) }else if( nRcvid ){ db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); while( db_step(&q)==SQLITE_ROW ){ @ %s(db_column_text(&q, 0)) } db_finalize(&q); } } @ </textarea> @ <input type="submit" name="add" value="Shun" /> @ </div></form> @ </blockquote> @ @ <a name="delshun"></a> @ <p>Enter the UUIDs of previously shunned artifacts to cause them to be @ accepted again in the repository. The artifacts content is not @ restored because the content is unknown. The only change is that @ the formerly shunned artifacts will be accepted on subsequent sync @ operations.</p> @ @ <blockquote> @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> login_insert_csrf_secret(); @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid"> if( zAccept ){ if( strlen(zAccept) ){ @ %h(zAccept) }else if( nRcvid ){ db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid); while( db_step(&q)==SQLITE_ROW ){ @ %s(db_column_text(&q, 0)) } db_finalize(&q); } } @ </textarea> @ <input type="submit" name="sub" value="Accept" /> @ </div></form> @ </blockquote> @ @ <p>Press the Rebuild button below to rebuild the repository. The @ content of newly shunned artifacts is not purged until the repository @ is rebuilt. On larger repositories, the rebuild may take minute or @ two, so be patient after pressing the button.</p> @ @ <blockquote> @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> login_insert_csrf_secret(); @ <input type="submit" name="rebuild" value="Rebuild" /> @ </div></form> @ </blockquote> @ @ <hr /><p>Shunned Artifacts:</p> @ <blockquote><p> db_prepare(&q, "SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)" " FROM shun ORDER BY uuid"); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); int stillExists = db_column_int(&q, 1); cnt++; if( stillExists ){ @ <b><a href="%R/artifact/%s(zUuid)">%s(zUuid)</a></b><br /> }else{ @ <b>%s(zUuid)</b><br /> } } if( cnt==0 ){ @ <i>no artifacts are shunned on this server</i> } db_finalize(&q); @ </p></blockquote> style_footer(); fossil_free(zCanonical); } /* ** Remove from the BLOB table all artifacts that are in the SHUN table. */ void shun_artifacts(void){ Stmt q; |
︙ | ︙ | |||
214 215 216 217 218 219 220 221 222 223 224 225 | /* ** WEBPAGE: rcvfromlist ** ** Show a listing of RCVFROM table entries. */ void rcvfromlist_page(void){ int ofst = atoi(PD("ofst","0")); int cnt; Stmt q; login_check_credentials(); if( !g.perm.Admin ){ | > | > | > > > > > > > > > | | > | | | > > | > | > > > > | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | /* ** WEBPAGE: rcvfromlist ** ** Show a listing of RCVFROM table entries. */ void rcvfromlist_page(void){ int ofst = atoi(PD("ofst","0")); int showAll = P("all")!=0; int cnt; Stmt q; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_header("Artifact Receipts"); if( showAll ){ ofst = 0; }else{ style_submenu_element("All", "All", "rcvfromlist?all=1"); } if( ofst>0 ){ style_submenu_element("Newer", "Newer", "rcvfromlist?ofst=%d", ofst>30 ? ofst-30 : 0); } db_multi_exec( "CREATE TEMP TABLE rcvidUsed(x INTEGER PRIMARY KEY);" "INSERT OR IGNORE INTO rcvidUsed(x) SELECT rcvid FROM blob;" ); db_prepare(&q, "SELECT rcvid, login, datetime(rcvfrom.mtime), rcvfrom.ipaddr," " EXISTS(SELECT 1 FROM rcvidUsed WHERE x=rcvfrom.rcvid)" " FROM rcvfrom LEFT JOIN user USING(uid)" " ORDER BY rcvid DESC LIMIT %d OFFSET %d", showAll ? -1 : 31, ofst ); @ <p>Whenever new artifacts are added to the repository, either by @ push or using the web interface, an entry is made in the RCVFROM table @ to record the source of that artifact. This log facilitates @ finding and fixing attempts to inject illicit content into the @ repository.</p> @ @ <p>Click on the "rcvid" to show a list of specific artifacts received @ by a transaction. After identifying illicit artifacts, remove them @ using the "Shun" button. If an "rcvid" is not hyperlinked, that means @ all artifacts associated with that rcvid have already been shunned @ or purged.</p> @ @ <table cellpadding="0" cellspacing="0" border="0"> @ <tr><th style="padding-right: 15px;text-align: right;">rcvid</th> @ <th style="padding-right: 15px;text-align: left;">Date</th> @ <th style="padding-right: 15px;text-align: left;">User</th> @ <th style="text-align: left;">IP Address</th></tr> cnt = 0; while( db_step(&q)==SQLITE_ROW ){ int rcvid = db_column_int(&q, 0); const char *zUser = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); const char *zIpAddr = db_column_text(&q, 3); if( cnt==30 && !showAll ){ style_submenu_element("Older", "Older", "rcvfromlist?ofst=%d", ofst+30); }else{ cnt++; @ <tr> if( db_column_int(&q,4) ){ @ <td style="padding-right: 15px;text-align: right;"> @ <a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td> }else{ @ <td style="padding-right: 15px;text-align: right;">%d(rcvid)</td> } @ <td style="padding-right: 15px;text-align: left;">%s(zDate)</td> @ <td style="padding-right: 15px;text-align: left;">%h(zUser)</td> @ <td style="text-align: left;">%s(zIpAddr)</td> @ </tr> } } db_finalize(&q); |
︙ | ︙ | |||
282 283 284 285 286 287 288 | */ void rcvfrom_page(void){ int rcvid = atoi(PD("rcvid","0")); Stmt q; login_check_credentials(); if( !g.perm.Admin ){ | | > | > > > > > > > > > > > > > > | > > > > > > > | < > > | | | 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | */ void rcvfrom_page(void){ int rcvid = atoi(PD("rcvid","0")); Stmt q; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_header("Artifact Receipt %d", rcvid); if( db_exists( "SELECT 1 FROM blob WHERE rcvid=%d AND" " NOT EXISTS (SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)", rcvid) ){ style_submenu_element("Shun All", "Shun All", "shun?shun&rcvid=%d#addshun", rcvid); } if( db_exists( "SELECT 1 FROM blob WHERE rcvid=%d AND" " EXISTS (SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)", rcvid) ){ style_submenu_element("Unshun All", "Unshun All", "shun?accept&rcvid=%d#delshun", rcvid); } db_prepare(&q, "SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr" " FROM rcvfrom LEFT JOIN user USING(uid)" " WHERE rcvid=%d", rcvid ); @ <table cellspacing="15" cellpadding="0" border="0"> @ <tr><th valign="top" align="right">rcvid:</th> @ <td valign="top">%d(rcvid)</td></tr> if( db_step(&q)==SQLITE_ROW ){ const char *zUser = db_column_text(&q, 0); const char *zDate = db_column_text(&q, 1); const char *zIpAddr = db_column_text(&q, 2); @ <tr><th valign="top" align="right">User:</th> @ <td valign="top">%s(zUser)</td></tr> @ <tr><th valign="top" align="right">Date:</th> @ <td valign="top">%s(zDate)</td></tr> @ <tr><th valign="top" align="right">IP Address:</th> @ <td valign="top">%s(zIpAddr)</td></tr> } db_finalize(&q); db_multi_exec( "CREATE TEMP TABLE toshow(rid INTEGER PRIMARY KEY);" "INSERT INTO toshow SELECT rid FROM blob WHERE rcvid=%d", rcvid ); describe_artifacts("IN toshow"); db_prepare(&q, "SELECT blob.rid, blob.uuid, blob.size, description.summary\n" " FROM blob LEFT JOIN description ON (blob.rid=description.rid)" " WHERE blob.rcvid=%d", rcvid ); @ <tr><th valign="top" align="right">Artifacts:</th> @ <td valign="top"> while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 1); int size = db_column_int(&q, 2); const char *zDesc = db_column_text(&q, 3); if( zDesc==0 ) zDesc = ""; @ <a href="%R/info/%s(zUuid)">%s(zUuid)</a> @ %h(zDesc) (size: %d(size))<br /> } @ </td></tr> @ </table> db_finalize(&q); style_footer(); } |
Added src/sitemap.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | /* ** Copyright (c) 2014 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement the sitemap webpage. */ #include "config.h" #include "sitemap.h" #include <assert.h> /* ** WEBPAGE: sitemap ** ** Show an incomplete list of web pages offered by the Fossil web engine. */ void sitemap_page(void){ login_check_credentials(); style_header("Site Map"); style_adunit_config(ADUNIT_RIGHT_OK); @ <p> @ The following links are just a few of the many web-pages available for @ this Fossil repository: @ </p> @ @ <ul> @ <li>%z(href("%R/home"))Home Page</a> @ <ul> @ <li>%z(href("%R/docsrc"))Search Project Documentation</a></li> @ </ul></li> @ <li>%z(href("%R/tree"))File Browser</a></li> @ <ul> @ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view, @ Trunk Check-in</a></li> @ <li>%z(href("%R/tree?type=flat"))Flat-view</a></li> @ <li>%z(href("%R/fileage?name=trunk"))File ages for Trunk</a></li> @ </ul> @ <li>%z(href("%R/timeline?n=200"))Project Timeline</a></li> @ <ul> @ <li>%z(href("%R/timeline?a=1970-01-01&y=ci&n=10"))First 10 @ check-ins</a></li> @ <li>%z(href("%R/timeline?n=all&namechng"))All check-ins with file name @ changes</a></li> @ <li>%z(href("%R/reports"))Activity Reports</a></li> @ </ul> @ <li>%z(href("%R/brlist"))Branches</a></li> @ <ul> @ <li>%z(href("%R/leaves"))Leaf Check-ins</a></li> @ <li>%z(href("%R/taglist"))List of Tags</a></li> @ </ul> @ </li> @ <li>%z(href("%R/wikihelp"))Wiki</a> @ <ul> @ <li>%z(href("%R/wikisrch"))Wiki Search</a></li> @ <li>%z(href("%R/wcontent"))List of Wiki Pages</a></li> @ <li>%z(href("%R/timeline?y=w"))Recent activity</a></li> @ <li>%z(href("%R/wiki_rules"))Wiki Formatting Rules</a></li> @ <li>%z(href("%R/md_rules"))Markdown Formatting Rules</a></li> @ <li>%z(href("%R/wiki?name=Sandbox"))Sandbox</a></li> @ <li>%z(href("%R/attachlist"))List of Attachments</a></li> @ </ul> @ </li> @ <li>%z(href("%R/reportlist"))Tickets</a> @ <ul> @ <li>%z(href("%R/tktsrch"))Ticket Search</a></li> @ <li>%z(href("%R/timeline?y=t"))Recent activity</a></li> @ <li>%z(href("%R/attachlist"))List of Attachments</a></li> @ </ul> @ </li> @ <li>%z(href("%R/search"))Full-Text Search</a></li> @ <li>%z(href("%R/login"))Login/Logout/Change Password</a></li> @ <li>%z(href("%R/stat"))Repository Status</a> @ <ul> @ <li>%z(href("%R/hash-collisions"))Collisions on SHA1 hash @ prefixes</a></li> @ <li>%z(href("%R/urllist"))List of URLs used to access @ this repository</a></li> @ <li>%z(href("%R/bloblist"))List of Artifacts</a></li> @ </ul></li> @ <li>On-line Documentation @ <ul> @ <li>%z(href("%R/help"))List of All Commands and Web Pages</a></li> @ <li>%z(href("%R/test-all-help"))All "help" text on a single page</a></li> @ <li>%z(href("%R/mimetype_list"))Filename suffix to mimetype map</a></li> @ </ul></li> @ <li>%z(href("%R/setup"))Administration Pages</a> @ <ul> @ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li> @ <li>%z(href("%R/admin_log"))Admin log</a></li> @ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li> @ </ul></li> @ <li>Test Pages @ <ul> @ <li>%z(href("%R/test_env"))CGI Environment Test</a></li> @ <li>%z(href("%R/test_timewarps"))List of "Timewarp" Check-ins</a></li> @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> @ <li>%z(href("%R/hash-color-test"))Page to experiment with the automatic @ colors assigned to branch names</a> @ </ul></li> @ </ul></li> style_footer(); } |
Changes to src/skins.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Implementation of the Setup page for "skins". */ | < > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > | | > | > > > > > > > > | | > | > > > > > > > > > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Implementation of the Setup page for "skins". */ #include "config.h" #include <assert.h> #include "skins.h" /* ** An array of available built-in skins. ** ** To add new built-in skins: ** ** 1. Pick a name for the new skin. (Here we use "xyzzy"). ** ** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt, ** and skins/xyzzy/footer.txt into the source tree. ** ** 3. Rerun "tclsh makemake.tcl" in the src/ folder in order to ** rebuild the makefiles to reference the new CSS, headers, and footers. ** ** 4. Make an entry in the following array for the new skin. */ static struct BuiltinSkin { const char *zDesc; /* Description of this skin */ const char *zLabel; /* The directory under skins/ holding this skin */ char *zSQL; /* Filled in at run-time with SQL to insert this skin */ } aBuiltinSkin[] = { { "Default", "default", 0 }, { "Blitz", "blitz", 0 }, { "Blitz, No Logo", "blitz_no_logo", 0 }, { "Xekri", "xekri", 0 }, { "Original", "original", 0 }, { "Enhanced Original", "enhanced1", 0 }, { "Shadow boxes & Rounded Corners", "rounded1", 0 }, { "Eagle", "eagle", 0 }, { "Black & White, Menu on Left", "black_and_white", 0 }, { "Plain Gray, No Logo", "plain_gray", 0 }, { "Khaki, No Logo", "khaki", 0 }, }; /* ** Alternative skins can be specified in the CGI script or by options ** on the "http", "ui", and "server" commands. The alternative skin ** name must be one of the aBuiltinSkin[].zLabel names. If there is ** a match, that alternative is used. ** ** The following static variable holds the name of the alternative skin, ** or NULL if the skin should be as configured. */ static struct BuiltinSkin *pAltSkin = 0; static char *zAltSkinDir = 0; /* ** Skin details are a set of key/value pairs that define display ** attributes of the skin that cannot be easily specified using CSS ** or that need to be known on the server-side. ** ** The following array holds the value for all known skin details. */ static struct SkinDetail { const char *zName; /* Name of the detail */ char *zValue; /* Value of the detail */ } aSkinDetail[] = { { "timeline-arrowheads", "1" }, { "timeline-circle-nodes", "0" }, { "timeline-color-graph-lines", "0" }, { "white-foreground", "0" }, }; /* ** Invoke this routine to set the alternative skin. Return NULL if the ** alternative was successfully installed. Return a string listing all ** available skins if zName does not match an available skin. Memory ** for the returned string comes from fossil_malloc() and should be freed ** by the caller. ** ** If the alternative skin name contains one or more '/' characters, then ** it is assumed to be a directory on disk that holds override css.txt, ** footer.txt, and header.txt. This mode can be used for interactive ** development of new skins. */ char *skin_use_alternative(const char *zName){ int i; Blob err = BLOB_INITIALIZER; if( strchr(zName, '/')!=0 ){ zAltSkinDir = fossil_strdup(zName); return 0; } for(i=0; i<ArraySize(aBuiltinSkin); i++){ if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){ pAltSkin = &aBuiltinSkin[i]; return 0; } } blob_appendf(&err, "available skins: %s", aBuiltinSkin[0].zLabel); for(i=1; i<ArraySize(aBuiltinSkin); i++){ blob_append(&err, " ", 1); blob_append(&err, aBuiltinSkin[i].zLabel, -1); } return blob_str(&err); } /* ** Look for the --skin command-line option and process it. Or ** call fossil_fatal() if an unknown skin is specified. */ void skin_override(void){ const char *zSkin = find_option("skin",0,1); if( zSkin ){ char *zErr = skin_use_alternative(zSkin); if( zErr ) fossil_fatal("%s", zErr); } } /* ** The following routines return the various components of the skin ** that should be used for the current run. */ const char *skin_get(const char *zWhat){ const char *zOut; char *z; if( zAltSkinDir ){ char *z = mprintf("%s/%s.txt", zAltSkinDir, zWhat); if( file_isfile(z) ){ Blob x; blob_read_from_file(&x, z); fossil_free(z); return blob_str(&x); } fossil_free(z); } if( pAltSkin ){ z = mprintf("skins/%s/%s.txt", pAltSkin->zLabel, zWhat); zOut = builtin_text(z); fossil_free(z); }else{ zOut = db_get(zWhat, 0); if( zOut==0 ){ z = mprintf("skins/default/%s.txt", zWhat); zOut = builtin_text(z); fossil_free(z); } } return zOut; } /* ** Return a pointer to a SkinDetail element. Return 0 if not found. */ static struct SkinDetail *skin_detail_find(const char *zName){ int lwr = 0; int upr = ArraySize(aSkinDetail); while( upr>=lwr ){ int mid = (upr+lwr)/2; int c = fossil_strcmp(aSkinDetail[mid].zName, zName); if( c==0 ) return &aSkinDetail[mid]; if( c<0 ){ lwr = mid+1; }else{ upr = mid-1; } } return 0; } /* Initialize the aSkinDetail array using the text in the details.txt ** file. */ static void skin_detail_initialize(void){ static int isInit = 0; char *zDetail; Blob detail, line, key, value; if( isInit ) return; isInit = 1; zDetail = (char*)skin_get("details"); if( zDetail==0 ) return; zDetail = fossil_strdup(zDetail); blob_init(&detail, zDetail, -1); while( blob_line(&detail, &line) ){ char *zKey; int nKey; struct SkinDetail *pDetail; if( !blob_token(&line, &key) ) continue; zKey = blob_buffer(&key); if( zKey[0]=='#' ) continue; nKey = blob_size(&key); if( nKey<2 ) continue; if( zKey[nKey-1]!=':' ) continue; zKey[nKey-1] = 0; pDetail = skin_detail_find(zKey); if( pDetail==0 ) continue; if( !blob_token(&line, &value) ) continue; pDetail->zValue = fossil_strdup(blob_str(&value)); } blob_reset(&detail); fossil_free(zDetail); } /* ** Return a skin detail setting */ const char *skin_detail(const char *zName){ struct SkinDetail *pDetail; skin_detail_initialize(); pDetail = skin_detail_find(zName); if( pDetail==0 ) fossil_fatal("no such skin detail: %s", zName); return pDetail->zValue; } int skin_detail_boolean(const char *zName){ return !is_false(skin_detail(zName)); } /* ** Hash function for computing a skin id. */ static unsigned int skin_hash(unsigned int h, const char *z){ if( z==0 ) return h; while( z[0] ){ h = (h<<11) ^ (h<<1) ^ (h>>3) ^ z[0]; z++; } return h; } /* ** Return an identifier that is (probably) different for every skin ** but that is (probably) the same if the skin is unchanged. This ** identifier can be attached to resource URLs to force reloading when ** the resources change but allow the resources to be read from cache ** as long as they are unchanged. */ unsigned int skin_id(const char *zResource){ unsigned int h = 0; if( zAltSkinDir ){ h = skin_hash(0, zAltSkinDir); }else if( pAltSkin ){ h = skin_hash(0, pAltSkin->zLabel); }else{ char *zMTime = db_get_mtime(zResource, 0, 0); h = skin_hash(0, zMTime); fossil_free(zMTime); } h = skin_hash(h, MANIFEST_UUID); return h; } /* ** For a skin named zSkinName, compute the name of the CONFIG table ** entry where that skin is stored and return it. ** ** Return NULL if zSkinName is NULL or an empty string. ** |
︙ | ︙ | |||
1273 1274 1275 1276 1277 1278 1279 | free(z); z = 0; } return z; } /* | > > > > | > > > > > | > > > > > | > > > > > | < > > > > | | < < | > | > > | | < > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > > > > > | < < < < < < < < < < | < | < < | > > > > | < | | | | | | > | | | | | | > | | > > > > > > > > | | > | | > | < | > > > | > > > > | > < < < | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 | free(z); z = 0; } return z; } /* ** Return true if there exists a skin name "zSkinName". */ static int skinExists(const char *zSkinName){ int i; if( zSkinName==0 ) return 0; for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ if( fossil_strcmp(zSkinName, aBuiltinSkin[i].zDesc)==0 ) return 1; } return db_exists("SELECT 1 FROM config WHERE name='skin:%q'", zSkinName); } /* ** Construct and return an string of SQL statements that represents ** a "skin" setting. If zName==0 then return the skin currently ** installed. Otherwise, return one of the built-in skins designated ** by zName. ** ** Memory to hold the returned string is obtained from malloc. */ static char *getSkin(const char *zName){ const char *z; char *zLabel; static const char *azType[] = { "css", "header", "footer", "details" }; int i; Blob val; blob_zero(&val); for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){ if( zName ){ zLabel = mprintf("skins/%s/%s.txt", zName, azType[i]); z = builtin_text(zLabel); fossil_free(zLabel); }else{ z = db_get(azType[i], 0); if( z==0 ){ zLabel = mprintf("skins/default/%s.txt", azType[i]); z = builtin_text(zLabel); fossil_free(zLabel); } } blob_appendf(&val, "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now());\n", azType[i], z ); } return blob_str(&val); } /* ** Respond to a Rename button press. Return TRUE if a dialog was painted. ** Return FALSE to continue with the main Skins page. */ static int skinRename(void){ const char *zOldName; const char *zNewName; int ex = 0; if( P("rename")==0 ) return 0; zOldName = P("sn"); zNewName = P("newname"); if( zOldName==0 ) return 0; if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){ if( zNewName==0 ) zNewName = zOldName; style_header("Rename A Skin"); if( ex ){ @ <p><span class="generalError">There is already another skin @ named "%h(zNewName)". Choose a different name.</span></p> } @ <form action="%s(g.zTop)/setup_skin" method="post"><div> @ <table border="0"><tr> @ <tr><td align="right">Current name:<td align="left"><b>%h(zOldName)</b> @ <tr><td align="right">New name:<td align="left"> @ <input type="text" size="35" name="newname" value="%h(zNewName)"> @ <tr><td><td> @ <input type="hidden" name="sn" value="%h(zOldName)"> @ <input type="submit" name="rename" value="Rename"> @ <input type="submit" name="canren" value="Cancel"> @ </table> login_insert_csrf_secret(); @ </div></form> style_footer(); return 1; } db_multi_exec( "UPDATE config SET name='skin:%q' WHERE name='skin:%q';", zNewName, zOldName ); return 0; } /* ** Respond to a Save button press. Return TRUE if a dialog was painted. ** Return FALSE to continue with the main Skins page. */ static int skinSave(const char *zCurrent){ const char *zNewName; int ex = 0; if( P("save")==0 ) return 0; zNewName = P("svname"); if( zNewName && zNewName[0]!=0 ){ } if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){ if( zNewName==0 ) zNewName = ""; style_header("Save Current Skin"); if( ex ){ @ <p><span class="generalError">There is already another skin @ named "%h(zNewName)". Choose a different name.</span></p> } @ <form action="%s(g.zTop)/setup_skin" method="post"><div> @ <table border="0"><tr> @ <tr><td align="right">Name for this skin:<td align="left"> @ <input type="text" size="35" name="svname" value="%h(zNewName)"> @ <tr><td><td> @ <input type="submit" name="save" value="Save"> @ <input type="submit" name="cansave" value="Cancel"> @ </table> login_insert_csrf_secret(); @ </div></form> style_footer(); return 1; } db_multi_exec( "INSERT OR IGNORE INTO config(name, value, mtime)" "VALUES('skin:%q',%Q,now())", zNewName, zCurrent ); return 0; } /* ** WEBPAGE: setup_skin */ void setup_skin(void){ const char *z; char *zName; char *zErr = 0; const char *zCurrent = 0; /* Current skin */ int i; /* Loop counter */ Stmt q; int seenCurrent = 0; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } db_begin_transaction(); zCurrent = getSkin(0); for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel); } /* Process requests to delete a user-defined skin */ if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ style_header("Confirm Custom Skin Delete"); @ <form action="%s(g.zTop)/setup_skin" method="post"><div> @ <p>Deletion of a custom skin is a permanent action that cannot @ be undone. Please confirm that this is what you want to do:</p> @ <input type="hidden" name="sn" value="%h(P("sn"))" /> @ <input type="submit" name="del2" value="Confirm - Delete The Skin" /> @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" /> login_insert_csrf_secret(); @ </div></form> style_footer(); return; } if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ db_multi_exec("DELETE FROM config WHERE name=%Q", zName); } if( skinRename() ) return; if( skinSave(zCurrent) ) return; /* The user pressed one of the "Install" buttons. */ if( P("load") && (z = P("sn"))!=0 && z[0] ){ int seen = 0; /* Check to see if the current skin is already saved. If it is, there ** is no need to create a backup */ zCurrent = getSkin(0); for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ seen = 1; break; } } if( !seen ){ seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" " AND value=%Q", zCurrent); if( !seen ){ db_multi_exec( "INSERT INTO config(name,value,mtime) VALUES(" " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," " %Q,now())", zCurrent ); } } seen = 0; for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ if( fossil_strcmp(aBuiltinSkin[i].zDesc, z)==0 ){ seen = 1; zCurrent = aBuiltinSkin[i].zSQL; db_multi_exec("%s", zCurrent/*safe-for-%s*/); break; } } if( !seen ){ zName = skinVarName(z,0); zCurrent = db_get(zName, 0); db_multi_exec("%s", zCurrent/*safe-for-%s*/); } } style_header("Skins"); if( zErr ){ @ <p><font color="red">%h(zErr)</font></p> } @ <p>A "skin" is a combination of @ <a href="setup_skinedit?w=0">CSS</a>, @ <a href="setup_skinedit?w=2">Header</a>, @ <a href="setup_skinedit?w=1">Footer</a>, and @ <a href="setup_skinedit?w=3">Details</a> @ that determines the look and feel @ of the web interface.</p> @ if( pAltSkin ){ @ <p class="generalError"> @ This page is generated using an skin override named @ "%h(pAltSkin->zLabel)". You can change the skin configuration @ below, but the changes will not take effect until the Fossil server @ is restarted without the override.</p> @ } @ <h2>Available Skins:</h2> @ <table border="0"> for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ z = aBuiltinSkin[i].zDesc; @ <tr><td>%d(i+1).<td>%h(z)<td> <td> if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ @ (Currently In Use) seenCurrent = 1; }else{ @ <form action="%s(g.zTop)/setup_skin" method="post"> @ <input type="hidden" name="sn" value="%h(z)" /> @ <input type="submit" name="load" value="Install" /> if( pAltSkin==&aBuiltinSkin[i] ){ @ (Current override) } @ </form> } @ </tr> } db_prepare(&q, "SELECT substr(name, 6), value FROM config" " WHERE name GLOB 'skin:*'" " ORDER BY name" ); while( db_step(&q)==SQLITE_ROW ){ const char *zN = db_column_text(&q, 0); const char *zV = db_column_text(&q, 1); i++; @ <tr><td>%d(i).<td>%h(zN)<td> <td> @ <form action="%s(g.zTop)/setup_skin" method="post"> if( fossil_strcmp(zV, zCurrent)==0 ){ @ (Currently In Use) seenCurrent = 1; }else{ @ <input type="submit" name="load" value="Install"> @ <input type="submit" name="del1" value="Delete"> } @ <input type="submit" name="rename" value="Rename"> @ <input type="hidden" name="sn" value="%h(zN)"> @ </form></tr> } db_finalize(&q); if( !seenCurrent ){ i++; @ <tr><td>%d(i).<td><i>Current Configuration</i><td> <td> @ <form action="%s(g.zTop)/setup_skin" method="post"> @ <input type="submit" name="save" value="Save"> @ </form> } @ </table> style_footer(); db_end_transaction(0); } /* ** WEBPAGE: setup_skinedit ** ** w=N -- 0=CSS, 1=footer, 2=header, 3=details */ void setup_skinedit(void){ static const struct sSkinAddr { const char *zFile; const char *zTitle; const char *zSubmenu; } aSkinAttr[] = { /* 0 */ { "css", "CSS", "CSS", }, /* 1 */ { "footer", "Page Footer", "Footer", }, /* 2 */ { "header", "Page Header", "Header", }, /* 3 */ { "details", "Display Details", "Details", }, }; const char *zBasis; const char *zContent; char *zDflt; int ii; int j; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } ii = atoi(PD("w","0")); if( ii<0 || ii>ArraySize(aSkinAttr) ) ii = 0; zBasis = PD("basis","default"); zDflt = mprintf("skins/%s/%s.txt", zBasis, aSkinAttr[ii].zFile); db_begin_transaction(); if( P("revert")!=0 ){ db_multi_exec("DELETE FROM config WHERE name=%Q", aSkinAttr[ii].zFile); cgi_replace_parameter(aSkinAttr[ii].zFile, builtin_text(zDflt)); } style_header("%s", aSkinAttr[ii].zTitle); for(j=0; j<ArraySize(aSkinAttr); j++){ if( j==ii ) continue; style_submenu_element(aSkinAttr[j].zSubmenu, 0, "%R/setup_skinedit?w=%d&basis=%h",j,zBasis); } style_submenu_element("Skins", 0, "%R/setup_skin"); @ <form action="%s(g.zTop)/setup_skinedit" method="post"><div> login_insert_csrf_secret(); @ <input type='hidden' name='w' value='%d(ii)'> @ <h2>Edit %s(aSkinAttr[ii].zTitle):</h2> zContent = textarea_attribute("", 10, 80, aSkinAttr[ii].zFile, aSkinAttr[ii].zFile, builtin_text(zDflt), 0); @ <br /> @ <input type="submit" name="submit" value="Apply Changes" /> @ <hr /> @ Baseline: <select size='1' name='basis'> for(j=0; j<ArraySize(aBuiltinSkin); j++){ cgi_printf("<option value='%h'%s>%h</option>\n", aBuiltinSkin[j].zLabel, fossil_strcmp(zBasis,aBuiltinSkin[j].zLabel)==0 ? " selected" : "", aBuiltinSkin[j].zDesc ); } @ </select> @ <input type="submit" name="diff" value="Diff" /> if( P("diff")!=0 ){ u64 diffFlags = construct_diff_flags(0,0) | DIFF_STRIP_EOLCR; Blob from, to, out; blob_init(&to, zContent, -1); blob_init(&from, builtin_text(zDflt), -1); blob_zero(&out); @ <input type="submit" name="revert" value="Revert" /><p> if( diffFlags & DIFF_SIDEBYSIDE ){ text_diff(&from, &to, &out, 0, diffFlags | DIFF_HTML | DIFF_NOTTOOBIG); @ %s(blob_str(&out)) }else{ text_diff(&from, &to, &out, 0, diffFlags | DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG); @ <pre class="udiff"> @ %s(blob_str(&out)) @ </pre> } blob_reset(&from); blob_reset(&to); blob_reset(&out); } @ </div></form> style_footer(); db_end_transaction(0); } |
Changes to src/sqlcmd.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This module contains the code that initializes the "sqlite3" command-line ** shell against the repository database. The command-line shell itself ** is a copy of the "shell.c" code from SQLite. This file contains logic ** to initialize the code in shell.c. */ #include "config.h" #include "sqlcmd.h" | > > > > | > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ** This module contains the code that initializes the "sqlite3" command-line ** shell against the repository database. The command-line shell itself ** is a copy of the "shell.c" code from SQLite. This file contains logic ** to initialize the code in shell.c. */ #include "config.h" #include "sqlcmd.h" #if defined(FOSSIL_ENABLE_MINIZ) # define MINIZ_HEADER_FILE_ONLY # include "miniz.c" #else # include <zlib.h> #endif /* ** Implementation of the "content(X)" SQL function. Return the complete ** content of artifact identified by X as a blob. */ static void sqlcmd_content( sqlite3_context *context, |
︙ | ︙ | |||
40 41 42 43 44 45 46 | zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 ) return; g.db = sqlite3_context_db_handle(context); g.repositoryOpen = 1; rid = name_to_rid(zName); if( rid==0 ) return; if( content_get(rid, &cx) ){ | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | zName = (const char*)sqlite3_value_text(argv[0]); if( zName==0 ) return; g.db = sqlite3_context_db_handle(context); g.repositoryOpen = 1; rid = name_to_rid(zName); if( rid==0 ) return; if( content_get(rid, &cx) ){ sqlite3_result_blob(context, blob_buffer(&cx), blob_size(&cx), SQLITE_TRANSIENT); blob_reset(&cx); } } /* ** Implementation of the "compress(X)" SQL function. The input X is |
︙ | ︙ | |||
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | rc = uncompress(pOut, &nOut, &pIn[4], nIn-4); if( rc==Z_OK ){ sqlite3_result_blob(context, pOut, nOut, sqlite3_free); }else{ sqlite3_result_error(context, "input is not zlib compressed", -1); } } /* ** This is the "automatic extension" initializer that runs right after ** the connection to the repository database is opened. Set up the ** database connection to be more useful to the human operator. */ static int sqlcmd_autoinit( sqlite3 *db, const char **pzErrMsg, const void *notUsed ){ | > > > > > > > > > > > > > > < | | < < < > > > < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | rc = uncompress(pOut, &nOut, &pIn[4], nIn-4); if( rc==Z_OK ){ sqlite3_result_blob(context, pOut, nOut, sqlite3_free); }else{ sqlite3_result_error(context, "input is not zlib compressed", -1); } } /* ** Add the content(), compress(), and decompress() SQL functions to ** database connection db. */ int add_content_sql_commands(sqlite3 *db){ sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, sqlcmd_content, 0, 0); sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0, sqlcmd_compress, 0, 0); sqlite3_create_function(db, "decompress", 1, SQLITE_UTF8, 0, sqlcmd_decompress, 0, 0); return SQLITE_OK; } /* ** This is the "automatic extension" initializer that runs right after ** the connection to the repository database is opened. Set up the ** database connection to be more useful to the human operator. */ static int sqlcmd_autoinit( sqlite3 *db, const char **pzErrMsg, const void *notUsed ){ add_content_sql_commands(db); db_add_aux_functions(db); re_add_sql_func(db); search_sql_setup(db); g.zMainDbType = "repository"; foci_register(db); g.repositoryOpen = 1; g.db = db; return SQLITE_OK; } /* ** COMMAND: sqlite3 ** ** Usage: %fossil sqlite3 ?DATABASE? ?OPTIONS? ** ** Run the standalone sqlite3 command-line shell on DATABASE with OPTIONS. ** If DATABASE is omitted, then the repository that serves the working ** directory is opened. See https://www.sqlite.org/cli.html for additional ** information. ** ** WARNING: Careless use of this command can corrupt a Fossil repository ** in ways that are unrecoverable. Be sure you know what you are doing before ** running any SQL commands that modifies the repository database. ** ** The following extensions to the usual SQLite commands are provided: ** ** content(X) Return the contenxt of artifact X. X can be a ** SHA1 hash or prefix or a tag. ** ** compress(X) Compress text X. ** ** decompress(X) Decompress text X. Undoes the work of ** compress(X). ** ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) ** found in check-in X (another BLOB.RID value). ** ** symbolic_name_to_rid(X) Return a the BLOB.RID corresponding to symbolic ** name X. ** ** now() Return the number of seconds since 1970. ** ** REGEXP The REGEXP operator works, unlike in ** standard SQLite. ** ** files_of_checkin The "files_of_check" virtual table is ** available for decoding manifests. ** ** Usage example for files_of_checkin: ** ** CREATE VIRTUAL TABLE temp.foci USING files_of_checkin; ** SELECT * FROM foci WHERE checkinID=symbolic_name_to_rid('trunk'); */ void cmd_sqlite3(void){ extern int sqlite3_shell(int, char**); db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); db_close(1); sqlite3_shutdown(); sqlite3_shell(g.argc-1, g.argv+1); sqlite3_cancel_auto_extension((void(*)(void))sqlcmd_autoinit); g.db = 0; g.zMainDbType = 0; g.repositoryOpen = 0; g.localOpen = 0; } /* ** This routine is called by the patched sqlite3 command-line shell in order ** to load the name and database connection for the open Fossil database. */ void fossil_open(const char **pzRepoName){ sqlite3_auto_extension((void(*)(void))sqlcmd_autoinit); *pzRepoName = g.zRepositoryName; } |
Changes to src/sqlite3.c.
more than 10,000 changes
Changes to src/sqlite3.h.
︙ | ︙ | |||
39 40 41 42 43 44 45 | */ #ifdef __cplusplus extern "C" { #endif /* | | < | > > > > > | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | */ #ifdef __cplusplus extern "C" { #endif /* ** Provide the ability to override linkage features of the interface. */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern #endif #ifndef SQLITE_API # define SQLITE_API #endif #ifndef SQLITE_CDECL # define SQLITE_CDECL #endif #ifndef SQLITE_STDCALL # define SQLITE_STDCALL #endif /* ** These no-op macros are used in front of interfaces to mark those ** interfaces as either deprecated or experimental. New applications ** should not use deprecated interfaces - they are supported for backwards ** compatibility only. Application writers should be aware that ** experimental interfaces are subject to change in point releases. ** ** These macros used to resolve to various kinds of compiler magic that ** would generate warning messages when they were used. But that ** compiler magic ended up generating such a flurry of bug reports ** that we have taken it all out and gone back to using simple |
︙ | ︙ | |||
103 104 105 106 107 108 109 | ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.8.9" #define SQLITE_VERSION_NUMBER 3008009 #define SQLITE_SOURCE_ID "2015-03-30 23:43:56 395bb3e677a6551b06ba96fc58c393132b93d1e8" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
︙ | ︙ | |||
138 139 140 141 142 143 144 | ** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns ** a pointer to a string constant whose value is the same as the ** [SQLITE_SOURCE_ID] C preprocessor macro. ** ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_API SQLITE_EXTERN const char sqlite3_version[]; | | | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns ** a pointer to a string constant whose value is the same as the ** [SQLITE_SOURCE_ID] C preprocessor macro. ** ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_API SQLITE_EXTERN const char sqlite3_version[]; SQLITE_API const char *SQLITE_STDCALL sqlite3_libversion(void); SQLITE_API const char *SQLITE_STDCALL sqlite3_sourceid(void); SQLITE_API int SQLITE_STDCALL sqlite3_libversion_number(void); /* ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the |
︙ | ︙ | |||
165 166 167 168 169 170 171 | ** and sqlite3_compileoption_get() may be omitted by specifying the ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS | | | | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | ** and sqlite3_compileoption_get() may be omitted by specifying the ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int SQLITE_STDCALL sqlite3_compileoption_used(const char *zOptName); SQLITE_API const char *SQLITE_STDCALL sqlite3_compileoption_get(int N); #endif /* ** CAPI3REF: Test To See If The Library Is Threadsafe ** ** ^The sqlite3_threadsafe() function returns zero if and only if ** SQLite was compiled with mutexing code omitted due to the |
︙ | ︙ | |||
197 198 199 200 201 202 203 | ** the desired setting of the [SQLITE_THREADSAFE] macro. ** ** This interface only reports on the compile-time mutex setting ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but ** can be fully or partially disabled using a call to [sqlite3_config()] ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], | | | | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | ** the desired setting of the [SQLITE_THREADSAFE] macro. ** ** This interface only reports on the compile-time mutex setting ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but ** can be fully or partially disabled using a call to [sqlite3_config()] ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], ** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the ** sqlite3_threadsafe() function shows only the compile-time setting of ** thread safety, not any run-time changes to that setting made by ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() ** is unchanged by calls to sqlite3_config().)^ ** ** See the [threading mode] documentation for additional information. */ SQLITE_API int SQLITE_STDCALL sqlite3_threadsafe(void); /* ** CAPI3REF: Database Connection Handle ** KEYWORDS: {database connection} {database connections} ** ** Each open SQLite database is represented by a pointer to an instance of ** the opaque structure named "sqlite3". It is useful to think of an sqlite3 |
︙ | ︙ | |||
265 266 267 268 269 270 271 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors ** for the [sqlite3] object. | | | | | | | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors ** for the [sqlite3] object. ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if ** the [sqlite3] object is successfully destroyed and all associated ** resources are deallocated. ** ** ^If the database connection is associated with unfinalized prepared ** statements or unfinished sqlite3_backup objects then sqlite3_close() ** will leave the database connection open and return [SQLITE_BUSY]. ** ^If sqlite3_close_v2() is called with unfinalized prepared statements ** and/or unfinished sqlite3_backups, then the database connection becomes ** an unusable "zombie" which will automatically be deallocated when the ** last prepared statement is finalized or the last sqlite3_backup is ** finished. The sqlite3_close_v2() interface is intended for use with ** host languages that are garbage collected, and where the order in which ** destructors are called is arbitrary. ** ** Applications should [sqlite3_finalize | finalize] all [prepared statements], ** [sqlite3_blob_close | close] all [BLOB handles], and ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated ** with the [sqlite3] object prior to attempting to close the object. ^If ** sqlite3_close_v2() is called on a [database connection] that still has ** outstanding [prepared statements], [BLOB handles], and/or ** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation ** of resources is deferred until all [prepared statements], [BLOB handles], ** and [sqlite3_backup] objects are also destroyed. ** ** ^If an [sqlite3] object is destroyed while a transaction is open, ** the transaction is automatically rolled back. ** ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] ** must be either a NULL ** pointer or an [sqlite3] object pointer obtained ** from [sqlite3_open()], [sqlite3_open16()], or ** [sqlite3_open_v2()], and not previously closed. ** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer ** argument is a harmless no-op. */ SQLITE_API int SQLITE_STDCALL sqlite3_close(sqlite3*); SQLITE_API int SQLITE_STDCALL sqlite3_close_v2(sqlite3*); /* ** The type for a callback function. ** This is legacy and deprecated. It is included for historical ** compatibility and is not documented. */ typedef int (*sqlite3_callback)(void*,int,char**, char**); |
︙ | ︙ | |||
366 367 368 369 370 371 372 | ** is not changed. ** ** Restrictions: ** ** <ul> ** <li> The application must insure that the 1st parameter to sqlite3_exec() ** is a valid and open [database connection]. | | | < | | < | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | ** is not changed. ** ** Restrictions: ** ** <ul> ** <li> The application must insure that the 1st parameter to sqlite3_exec() ** is a valid and open [database connection]. ** <li> The application must not close the [database connection] specified by ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. ** <li> The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ** </ul> */ SQLITE_API int SQLITE_STDCALL sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ void *, /* 1st argument to callback */ char **errmsg /* Error msg written here */ ); /* ** CAPI3REF: Result Codes ** KEYWORDS: {result code definitions} ** ** Many SQLite functions return an integer result code from the set shown ** here in order to indicate success or failure. ** ** New error codes may be added in future versions of SQLite. ** ** See also: [extended result code definitions] */ #define SQLITE_OK 0 /* Successful result */ /* beginning-of-error-codes */ #define SQLITE_ERROR 1 /* SQL error or missing database */ #define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ #define SQLITE_PERM 3 /* Access permission denied */ #define SQLITE_ABORT 4 /* Callback routine requested an abort */ |
︙ | ︙ | |||
429 430 431 432 433 434 435 | #define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ #define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ #define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ /* end-of-error-codes */ /* ** CAPI3REF: Extended Result Codes | < | | | | | | | < < < < < < | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 | #define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ #define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ #define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ /* end-of-error-codes */ /* ** CAPI3REF: Extended Result Codes ** KEYWORDS: {extended result code definitions} ** ** In its default configuration, SQLite API routines return one of 30 integer ** [result codes]. However, experience has shown that many of ** these result codes are too coarse-grained. They do not provide as ** much information about problems as programmers might like. In an effort to ** address this, newer versions of SQLite (version 3.3.8 and later) include ** support for additional result codes that provide more detailed information ** about errors. These [extended result codes] are enabled or disabled ** on a per database connection basis using the ** [sqlite3_extended_result_codes()] API. Or, the extended code for ** the most recent error can be obtained using ** [sqlite3_extended_errcode()]. */ #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) #define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) |
︙ | ︙ | |||
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) #define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) #define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8)) #define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8)) #define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8)) #define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8)) #define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) #define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) #define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) #define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) /* ** CAPI3REF: Flags For File Open Operations ** ** These bit values are intended for use in the ** 3rd parameter to the [sqlite3_open_v2()] interface and ** in the 4th parameter to the [sqlite3_vfs.xOpen] method. | > > > | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8)) #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8)) #define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) #define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8)) #define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8)) #define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8)) #define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8)) #define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) #define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) #define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) #define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) #define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) #define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) /* ** CAPI3REF: Flags For File Open Operations ** ** These bit values are intended for use in the ** 3rd parameter to the [sqlite3_open_v2()] interface and ** in the 4th parameter to the [sqlite3_vfs.xOpen] method. |
︙ | ︙ | |||
553 554 555 556 557 558 559 | ** first then the size of the file is extended, never the other ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that ** information is written to disk in the same order as calls ** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that ** after reboot following a crash or power loss, the only bytes in a ** file that were written at the application level might have changed ** and that adjacent bytes, even bytes within the same sector are | | > > > > > | 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 | ** first then the size of the file is extended, never the other ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that ** information is written to disk in the same order as calls ** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that ** after reboot following a crash or power loss, the only bytes in a ** file that were written at the application level might have changed ** and that adjacent bytes, even bytes within the same sector are ** guaranteed to be unchanged. The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN ** flag indicate that a file cannot be deleted when open. The ** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on ** read-only media and cannot be changed even by processes with ** elevated privileges. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. |
︙ | ︙ | |||
674 675 676 677 678 679 680 | ** integer opcode. The third argument is a generic pointer intended to ** point to a structure that may contain arguments or space in which to ** write return values. Potential uses for xFileControl() might be ** functions to enable blocking locks with timeouts, to change the ** locking strategy (for example to use dot-file locks), to inquire ** about the status of a lock, or to break stale locks. The SQLite ** core reserves all opcodes less than 100 for its own use. | | | 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 | ** integer opcode. The third argument is a generic pointer intended to ** point to a structure that may contain arguments or space in which to ** write return values. Potential uses for xFileControl() might be ** functions to enable blocking locks with timeouts, to change the ** locking strategy (for example to use dot-file locks), to inquire ** about the status of a lock, or to break stale locks. The SQLite ** core reserves all opcodes less than 100 for its own use. ** A [file control opcodes | list of opcodes] less than 100 is available. ** Applications that define a custom xFileControl method should use opcodes ** greater than 100 to avoid conflicts. VFS implementations should ** return [SQLITE_NOTFOUND] for file control opcodes that they do not ** recognize. ** ** The xSectorSize() method returns the sector size of the ** device that underlies the file. The sector size is the |
︙ | ︙ | |||
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void *p); /* Methods above are valid for version 3 */ /* Additional methods may be added in future releases */ }; /* ** CAPI3REF: Standard File Control Opcodes ** ** These integer constants are opcodes for the xFileControl method ** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] ** interface. ** ** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This ** opcode causes the xFileControl method to write the current state of ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) ** into an integer that the pArg argument points to. This capability | > > > | | | | 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 | int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void *p); /* Methods above are valid for version 3 */ /* Additional methods may be added in future releases */ }; /* ** CAPI3REF: Standard File Control Opcodes ** KEYWORDS: {file control opcodes} {file control opcode} ** ** These integer constants are opcodes for the xFileControl method ** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] ** interface. ** ** <ul> ** <li>[[SQLITE_FCNTL_LOCKSTATE]] ** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This ** opcode causes the xFileControl method to write the current state of ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) ** into an integer that the pArg argument points to. This capability ** is used during testing and is only available when the SQLITE_TEST ** compile-time option is used. ** ** <li>[[SQLITE_FCNTL_SIZE_HINT]] ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. |
︙ | ︙ | |||
784 785 786 787 788 789 790 | ** <li>[[SQLITE_FCNTL_FILE_POINTER]] ** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer ** to the [sqlite3_file] object associated with a particular database ** connection. See the [sqlite3_file_control()] documentation for ** additional information. ** ** <li>[[SQLITE_FCNTL_SYNC_OMITTED]] | > > > | | | < > | > > > > | | | > > > > > > > | | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 | ** <li>[[SQLITE_FCNTL_FILE_POINTER]] ** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer ** to the [sqlite3_file] object associated with a particular database ** connection. See the [sqlite3_file_control()] documentation for ** additional information. ** ** <li>[[SQLITE_FCNTL_SYNC_OMITTED]] ** No longer in use. ** ** <li>[[SQLITE_FCNTL_SYNC]] ** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and ** sent to the VFS immediately before the xSync method is invoked on a ** database file descriptor. Or, if the xSync method is not invoked ** because the user has configured SQLite with ** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place ** of the xSync method. In most cases, the pointer argument passed with ** this file-control is NULL. However, if the database file is being synced ** as part of a multi-database commit, the argument points to a nul-terminated ** string containing the transactions master-journal file name. VFSes that ** do not need this signal should silently ignore this opcode. Applications ** should not call [sqlite3_file_control()] with this opcode as doing so may ** disrupt the operation of the specialized VFSes that do require it. ** ** <li>[[SQLITE_FCNTL_COMMIT_PHASETWO]] ** The [SQLITE_FCNTL_COMMIT_PHASETWO] opcode is generated internally by SQLite ** and sent to the VFS after a transaction has been committed immediately ** but before the database is unlocked. VFSes that do not need this signal ** should silently ignore this opcode. Applications should not call ** [sqlite3_file_control()] with this opcode as doing so may disrupt the ** operation of the specialized VFSes that do require it. ** ** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]] ** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic ** retry counts and intervals for certain disk I/O operations for the ** windows [VFS] in order to provide robustness in the presence of ** anti-virus programs. By default, the windows VFS will retry file read, ** file write, and file delete operations up to 10 times, with a delay |
︙ | ︙ | |||
870 871 872 873 874 875 876 | ** of the char** argument point to a string obtained from [sqlite3_mprintf()] ** or the equivalent and that string will become the result of the pragma or ** the error message if the pragma fails. ^If the ** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal ** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA] ** file control returns [SQLITE_OK], then the parser assumes that the ** VFS has handled the PRAGMA itself and the parser generates a no-op | > > | | 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 | ** of the char** argument point to a string obtained from [sqlite3_mprintf()] ** or the equivalent and that string will become the result of the pragma or ** the error message if the pragma fails. ^If the ** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal ** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA] ** file control returns [SQLITE_OK], then the parser assumes that the ** VFS has handled the PRAGMA itself and the parser generates a no-op ** prepared statement if result string is NULL, or that returns a copy ** of the result string if the string is non-NULL. ** ^If the [SQLITE_FCNTL_PRAGMA] file control returns ** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means ** that the VFS encountered an error while handling the [PRAGMA] and the ** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] ** file control occurs at the beginning of pragma statement analysis and so ** it is able to override built-in [PRAGMA] statements. ** ** <li>[[SQLITE_FCNTL_BUSYHANDLER]] |
︙ | ︙ | |||
908 909 910 911 912 913 914 915 916 917 | ** The argument is a pointer to a value of type sqlite3_int64 that ** is an advisory maximum number of bytes in the file to memory map. The ** pointer is overwritten with the old value. The limit is not changed if ** the value originally pointed to is negative, and so the current limit ** can be queried by passing in a pointer to a negative number. This ** file-control is used internally to implement [PRAGMA mmap_size]. ** ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > | 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 | ** The argument is a pointer to a value of type sqlite3_int64 that ** is an advisory maximum number of bytes in the file to memory map. The ** pointer is overwritten with the old value. The limit is not changed if ** the value originally pointed to is negative, and so the current limit ** can be queried by passing in a pointer to a negative number. This ** file-control is used internally to implement [PRAGMA mmap_size]. ** ** <li>[[SQLITE_FCNTL_TRACE]] ** The [SQLITE_FCNTL_TRACE] file control provides advisory information ** to the VFS about what the higher layers of the SQLite stack are doing. ** This file control is used by some VFS activity tracing [shims]. ** The argument is a zero-terminated string. Higher layers in the ** SQLite stack may generate instances of this file control if ** the [SQLITE_USE_FCNTL_TRACE] compile-time option is enabled. ** ** <li>[[SQLITE_FCNTL_HAS_MOVED]] ** The [SQLITE_FCNTL_HAS_MOVED] file control interprets its argument as a ** pointer to an integer and it writes a boolean into that integer depending ** on whether or not the file has been renamed, moved, or deleted since it ** was first opened. ** ** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]] ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This ** opcode causes the xFileControl method to swap the file handle with the one ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might ** be advantageous to block on the next WAL lock if the lock is not immediately ** available. The WAL subsystem issues this signal during rare ** circumstances in order to fix a problem with priority inversion. ** Applications should <em>not</em> use this file-control. ** ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 #define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 #define SQLITE_FCNTL_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 #define SQLITE_FCNTL_SYNC_OMITTED 8 #define SQLITE_FCNTL_WIN32_AV_RETRY 9 #define SQLITE_FCNTL_PERSIST_WAL 10 #define SQLITE_FCNTL_OVERWRITE 11 #define SQLITE_FCNTL_VFSNAME 12 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 #define SQLITE_FCNTL_PRAGMA 14 #define SQLITE_FCNTL_BUSYHANDLER 15 #define SQLITE_FCNTL_TEMPFILENAME 16 #define SQLITE_FCNTL_MMAP_SIZE 18 #define SQLITE_FCNTL_TRACE 19 #define SQLITE_FCNTL_HAS_MOVED 20 #define SQLITE_FCNTL_SYNC 21 #define SQLITE_FCNTL_COMMIT_PHASETWO 22 #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 #define SQLITE_FCNTL_WAL_BLOCK 24 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only |
︙ | ︙ | |||
1178 1179 1180 1181 1182 1183 1184 | ** <li> SQLITE_SHM_LOCK | SQLITE_SHM_SHARED ** <li> SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE ** <li> SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED ** <li> SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE ** </ul> ** ** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as | | | 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 | ** <li> SQLITE_SHM_LOCK | SQLITE_SHM_SHARED ** <li> SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE ** <li> SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED ** <li> SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE ** </ul> ** ** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as ** was given on the corresponding lock. ** ** The xShmLock method can transition between unlocked and SHARED or ** between unlocked and EXCLUSIVE. It cannot transition between SHARED ** and EXCLUSIVE. */ #define SQLITE_SHM_UNLOCK 1 #define SQLITE_SHM_LOCK 2 |
︙ | ︙ | |||
1275 1276 1277 1278 1279 1280 1281 | ** (using the [SQLITE_OS_OTHER=1] compile-time ** option) the application must supply a suitable implementation for ** sqlite3_os_init() and sqlite3_os_end(). An application-supplied ** implementation of sqlite3_os_init() or sqlite3_os_end() ** must return [SQLITE_OK] on success and some other [error code] upon ** failure. */ | | | | | | 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 | ** (using the [SQLITE_OS_OTHER=1] compile-time ** option) the application must supply a suitable implementation for ** sqlite3_os_init() and sqlite3_os_end(). An application-supplied ** implementation of sqlite3_os_init() or sqlite3_os_end() ** must return [SQLITE_OK] on success and some other [error code] upon ** failure. */ SQLITE_API int SQLITE_STDCALL sqlite3_initialize(void); SQLITE_API int SQLITE_STDCALL sqlite3_shutdown(void); SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void); SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void); /* ** CAPI3REF: Configuring The SQLite Library ** ** The sqlite3_config() interface is used to make global configuration ** changes to SQLite in order to tune SQLite to the specific needs of ** the application. The default configuration is recommended for most |
︙ | ︙ | |||
1309 1310 1311 1312 1313 1314 1315 | ** vary depending on the [configuration option] ** in the first argument. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ | | | | 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 | ** vary depending on the [configuration option] ** in the first argument. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ SQLITE_API int SQLITE_CDECL sqlite3_config(int, ...); /* ** CAPI3REF: Configure database connections ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single ** [database connection] (specified in the first argument). ** ** The second argument to sqlite3_db_config(D,V,...) is the ** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code ** that indicates what aspect of the [database connection] is being configured. ** Subsequent arguments vary depending on the configuration verb. ** ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. */ SQLITE_API int SQLITE_CDECL sqlite3_db_config(sqlite3*, int op, ...); /* ** CAPI3REF: Memory Allocation Routines ** ** An instance of this object defines the interface between SQLite ** and low-level memory allocation routines. ** |
︙ | ︙ | |||
1371 1372 1373 1374 1375 1376 1377 | ** a memory allocation given a particular requested size. Most memory ** allocators round up memory allocations at least to the next multiple ** of 8. Some allocators round up to a larger multiple or to a power of 2. ** Every memory allocation request coming in through [sqlite3_malloc()] ** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, ** that causes the corresponding memory allocation to fail. ** | | | 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 | ** a memory allocation given a particular requested size. Most memory ** allocators round up memory allocations at least to the next multiple ** of 8. Some allocators round up to a larger multiple or to a power of 2. ** Every memory allocation request coming in through [sqlite3_malloc()] ** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, ** that causes the corresponding memory allocation to fail. ** ** The xInit method initializes the memory allocator. For example, ** it might allocate any require mutexes or initialize internal data ** structures. The xShutdown method is invoked (indirectly) by ** [sqlite3_shutdown()] and should deallocate any resources acquired ** by xInit. The pAppData pointer is used as the only parameter to ** xInit and xShutdown. ** ** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes |
︙ | ︙ | |||
1461 1462 1463 1464 1465 1466 1467 | ** ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** it is not possible to set the Serialized [threading mode] and ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_SERIALIZED configuration option.</dd> ** ** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt> | | | > | | > | | | | | | > | | < | < | | > | > > > > > > | | > | > | > | | > > | | > | > | < < < | | > | > > > > | | < | | | | | | | | | | | | | | | | | | | | | 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 | ** ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** it is not possible to set the Serialized [threading mode] and ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_SERIALIZED configuration option.</dd> ** ** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt> ** <dd> ^(The SQLITE_CONFIG_MALLOC option takes a single argument which is ** a pointer to an instance of the [sqlite3_mem_methods] structure. ** The argument specifies ** alternative low-level memory allocation routines to be used in place of ** the memory allocation routines built into SQLite.)^ ^SQLite makes ** its own private copy of the content of the [sqlite3_mem_methods] structure ** before the [sqlite3_config()] call returns.</dd> ** ** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt> ** <dd> ^(The SQLITE_CONFIG_GETMALLOC option takes a single argument which ** is a pointer to an instance of the [sqlite3_mem_methods] structure. ** The [sqlite3_mem_methods] ** structure is filled with the currently defined memory allocation routines.)^ ** This option can be used to overload the default memory allocation ** routines with a wrapper that simulations memory allocation failure or ** tracks memory usage, for example. </dd> ** ** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt> ** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int, ** interpreted as a boolean, which enables or disables the collection of ** memory allocation statistics. ^(When memory allocation statistics are ** disabled, the following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] ** <li> [sqlite3_soft_heap_limit64()] ** <li> [sqlite3_status64()] ** </ul>)^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory ** allocation statistics are disabled by default. ** </dd> ** ** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt> ** <dd> ^The SQLITE_CONFIG_SCRATCH option specifies a static memory buffer ** that SQLite can use for scratch memory. ^(There are three arguments ** to SQLITE_CONFIG_SCRATCH: A pointer an 8-byte ** aligned memory buffer from which the scratch allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N).)^ ** The first argument must be a pointer to an 8-byte aligned buffer ** of at least sz*N bytes of memory. ** ^SQLite will not use more than one scratch buffers per thread. ** ^SQLite will never request a scratch buffer that is more than 6 ** times the database page size. ** ^If SQLite needs needs additional ** scratch memory beyond what is provided by this configuration option, then ** [sqlite3_malloc()] will be used to obtain the memory needed.<p> ** ^When the application provides any amount of scratch memory using ** SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary large ** [sqlite3_malloc|heap allocations]. ** This can help [Robson proof|prevent memory allocation failures] due to heap ** fragmentation in low-memory embedded systems. ** </dd> ** ** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt> ** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a static memory buffer ** that SQLite can use for the database page cache with the default page ** cache implementation. ** This configuration should not be used if an application-define page ** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2] ** configuration option. ** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to ** 8-byte aligned ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page ** (a power of two between 512 and 65536) plus some extra bytes for each ** page header. ^The number of extra bytes needed by the page header ** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option ** to [sqlite3_config()]. ** ^It is harmless, apart from the wasted memory, ** for the sz parameter to be larger than necessary. The first ** argument should pointer to an 8-byte aligned block of memory that ** is at least sz*N bytes of memory, otherwise subsequent behavior is ** undefined. ** ^SQLite will use the memory provided by the first argument to satisfy its ** memory needs for the first N pages that it adds to cache. ^If additional ** page cache memory is needed beyond what is provided by this option, then ** SQLite goes to [sqlite3_malloc()] for the additional storage space.</dd> ** ** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt> ** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer ** that SQLite will use for all of its dynamic memory allocation needs ** beyond those provided for by [SQLITE_CONFIG_SCRATCH] and ** [SQLITE_CONFIG_PAGECACHE]. ** ^The SQLITE_CONFIG_HEAP option is only available if SQLite is compiled ** with either [SQLITE_ENABLE_MEMSYS3] or [SQLITE_ENABLE_MEMSYS5] and returns ** [SQLITE_ERROR] if invoked otherwise. ** ^There are three arguments to SQLITE_CONFIG_HEAP: ** An 8-byte aligned pointer to the memory, ** the number of bytes in the memory buffer, and the minimum allocation size. ** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts ** to using its default memory allocator (the system malloc() implementation), ** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the ** memory pointer is not NULL then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte ** boundary or subsequent behavior of SQLite will be undefined. ** The minimum allocation size is capped at 2**12. Reasonable values ** for the minimum allocation size are 2**5 through 2**8.</dd> ** ** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt> ** <dd> ^(The SQLITE_CONFIG_MUTEX option takes a single argument which is a ** pointer to an instance of the [sqlite3_mutex_methods] structure. ** The argument specifies alternative low-level mutex routines to be used ** in place the mutex routines built into SQLite.)^ ^SQLite makes a copy of ** the content of the [sqlite3_mutex_methods] structure before the call to ** [sqlite3_config()] returns. ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** the entire mutexing subsystem is omitted from the build and hence calls to ** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will ** return [SQLITE_ERROR].</dd> ** ** [[SQLITE_CONFIG_GETMUTEX]] <dt>SQLITE_CONFIG_GETMUTEX</dt> ** <dd> ^(The SQLITE_CONFIG_GETMUTEX option takes a single argument which ** is a pointer to an instance of the [sqlite3_mutex_methods] structure. The ** [sqlite3_mutex_methods] ** structure is filled with the currently defined mutex routines.)^ ** This option can be used to overload the default mutex allocation ** routines with a wrapper used to track mutex usage for performance ** profiling or testing, for example. ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** the entire mutexing subsystem is omitted from the build and hence calls to ** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will ** return [SQLITE_ERROR].</dd> ** ** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt> ** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine ** the default size of lookaside memory on each [database connection]. ** The first argument is the ** size of each lookaside buffer slot and the second is the number of ** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE ** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] ** option to [sqlite3_db_config()] can be used to change the lookaside ** configuration on individual connections.)^ </dd> ** ** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt> ** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is ** a pointer to an [sqlite3_pcache_methods2] object. This object specifies ** the interface to a custom page cache implementation.)^ ** ^SQLite makes a copy of the [sqlite3_pcache_methods2] object.</dd> ** ** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt> ** <dd> ^(The SQLITE_CONFIG_GETPCACHE2 option takes a single argument which ** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies of ** the current page cache implementation into that object.)^ </dd> ** ** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt> ** <dd> The SQLITE_CONFIG_LOG option is used to configure the SQLite ** global [error log]. ** (^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a ** function with a call signature of void(*)(void*,int,const char*), ** and a pointer to void. ^If the function pointer is not NULL, it is |
︙ | ︙ | |||
1613 1614 1615 1616 1617 1618 1619 | ** log message after formatting via [sqlite3_snprintf()]. ** The SQLite logging interface is not reentrant; the logger function ** supplied by the application must not invoke any SQLite interface. ** In a multi-threaded application, the application-defined logger ** function must be threadsafe. </dd> ** ** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI | | | | | > | | | | | > | | | 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 | ** log message after formatting via [sqlite3_snprintf()]. ** The SQLite logging interface is not reentrant; the logger function ** supplied by the application must not invoke any SQLite interface. ** In a multi-threaded application, the application-defined logger ** function must be threadsafe. </dd> ** ** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI ** <dd>^(The SQLITE_CONFIG_URI option takes a single argument of type int. ** If non-zero, then URI handling is globally enabled. If the parameter is zero, ** then URI handling is globally disabled.)^ ^If URI handling is globally ** enabled, all filenames passed to [sqlite3_open()], [sqlite3_open_v2()], ** [sqlite3_open16()] or ** specified as part of [ATTACH] commands are interpreted as URIs, regardless ** of whether or not the [SQLITE_OPEN_URI] flag is set when the database ** connection is opened. ^If it is globally disabled, filenames are ** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the ** database connection is opened. ^(By default, URI handling is globally ** disabled. The default value may be changed by compiling with the ** [SQLITE_USE_URI] symbol defined.)^ ** ** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN ** <dd>^The SQLITE_CONFIG_COVERING_INDEX_SCAN option takes a single integer ** argument which is interpreted as a boolean in order to enable or disable ** the use of covering indices for full table scans in the query optimizer. ** ^The default setting is determined ** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on" ** if that compile-time option is omitted. ** The ability to disable the use of covering indices for full table scans ** is because some incorrectly coded legacy applications might malfunction ** when the optimization is enabled. Providing the ability to ** disable the optimization allows the older, buggy application code to work ** without change even with newer versions of SQLite. ** ** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]] ** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE ** <dd> These options are obsolete and should not be used by new code. ** They are retained for backwards compatibility but are now no-ops. |
︙ | ︙ | |||
1662 1663 1664 1665 1666 1667 1668 | ** the connection being passed as the second parameter is being closed. The ** third parameter is passed NULL In this case. An example of using this ** configuration option can be seen in the "test_sqllog.c" source file in ** the canonical SQLite source tree.</dd> ** ** [[SQLITE_CONFIG_MMAP_SIZE]] ** <dt>SQLITE_CONFIG_MMAP_SIZE | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > | 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 | ** the connection being passed as the second parameter is being closed. The ** third parameter is passed NULL In this case. An example of using this ** configuration option can be seen in the "test_sqllog.c" source file in ** the canonical SQLite source tree.</dd> ** ** [[SQLITE_CONFIG_MMAP_SIZE]] ** <dt>SQLITE_CONFIG_MMAP_SIZE ** <dd>^SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values ** that are the default mmap size limit (the default setting for ** [PRAGMA mmap_size]) and the maximum allowed mmap size limit. ** ^The default setting can be overridden by each database connection using ** either the [PRAGMA mmap_size] command, or by using the ** [SQLITE_FCNTL_MMAP_SIZE] file control. ^(The maximum allowed mmap size ** will be silently truncated if necessary so that it does not exceed the ** compile-time maximum mmap size set by the ** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^ ** ^If either argument to this option is negative, then that argument is ** changed to its compile-time default. ** ** [[SQLITE_CONFIG_WIN32_HEAPSIZE]] ** <dt>SQLITE_CONFIG_WIN32_HEAPSIZE ** <dd>^The SQLITE_CONFIG_WIN32_HEAPSIZE option is only available if SQLite is ** compiled for Windows with the [SQLITE_WIN32_MALLOC] pre-processor macro ** defined. ^SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value ** that specifies the maximum size of the created heap. ** ** [[SQLITE_CONFIG_PCACHE_HDRSZ]] ** <dt>SQLITE_CONFIG_PCACHE_HDRSZ ** <dd>^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which ** is a pointer to an integer and writes into that integer the number of extra ** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE]. ** The amount of extra space required can change depending on the compiler, ** target platform, and SQLite version. ** ** [[SQLITE_CONFIG_PMASZ]] ** <dt>SQLITE_CONFIG_PMASZ ** <dd>^The SQLITE_CONFIG_PMASZ option takes a single parameter which ** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded ** sorter to that integer. The default minimum PMA Size is set by the ** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched ** to help with sort operations when multithreaded sorting ** is enabled (using the [PRAGMA threads] command) and the amount of content ** to be sorted exceeds the page size times the minimum of the ** [PRAGMA cache_size] setting and this value. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ |
︙ | ︙ | |||
1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 | #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ #define SQLITE_CONFIG_URI 17 /* int */ #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** | > > > | 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 | #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ #define SQLITE_CONFIG_URI 17 /* int */ #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** |
︙ | ︙ | |||
1768 1769 1770 1771 1772 1773 1774 | /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result ** codes are disabled by default for historical compatibility. */ | | > | | | | | | | > | 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 | /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result ** codes are disabled by default for historical compatibility. */ SQLITE_API int SQLITE_STDCALL sqlite3_extended_result_codes(sqlite3*, int onoff); /* ** CAPI3REF: Last Insert Rowid ** ** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables) ** has a unique 64-bit signed ** integer key called the [ROWID | "rowid"]. ^The rowid is always available ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those ** names are not also used by explicitly declared columns. ^If ** the table has a column of type [INTEGER PRIMARY KEY] then that column ** is another alias for the rowid. ** ** ^The sqlite3_last_insert_rowid(D) interface returns the [rowid] of the ** most recent successful [INSERT] into a rowid table or [virtual table] ** on database connection D. ** ^Inserts into [WITHOUT ROWID] tables are not recorded. ** ^If no successful [INSERT]s into rowid tables ** have ever occurred on the database connection D, ** then sqlite3_last_insert_rowid(D) returns zero. ** ** ^(If an [INSERT] occurs within a trigger or within a [virtual table] ** method, then this routine will return the [rowid] of the inserted ** row as long as the trigger or virtual table method is running. ** But once the trigger or virtual table method ends, the value returned ** by this routine reverts to what it was before the trigger or virtual ** table method began.)^ |
︙ | ︙ | |||
1817 1818 1819 1820 1821 1822 1823 | ** If a separate thread performs a new [INSERT] on the same ** database connection while the [sqlite3_last_insert_rowid()] ** function is running and thus changes the last insert [rowid], ** then the value returned by [sqlite3_last_insert_rowid()] is ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ | | | | | > > > | | | < < | | > > > | < < < < > > | > | < < < < < < < | > | < > | < | > > > > | | | | > | | < | < < | | | > > | < > | < < | | | < < < | | | 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 | ** If a separate thread performs a new [INSERT] on the same ** database connection while the [sqlite3_last_insert_rowid()] ** function is running and thus changes the last insert [rowid], ** then the value returned by [sqlite3_last_insert_rowid()] is ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_last_insert_rowid(sqlite3*); /* ** CAPI3REF: Count The Number Of Rows Modified ** ** ^This function returns the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. ** ^Executing any other type of SQL statement does not modify the value ** returned by this function. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], ** [foreign key actions] or [REPLACE] constraint resolution are not counted. ** ** Changes to a view that are intercepted by ** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value ** returned by sqlite3_changes() immediately after an INSERT, UPDATE or ** DELETE statement run on a view is always zero. Only changes made to real ** tables are counted. ** ** Things are more complicated if the sqlite3_changes() function is ** executed while a trigger program is running. This may happen if the ** program uses the [changes() SQL function], or if some other callback ** function invokes sqlite3_changes() directly. Essentially: ** ** <ul> ** <li> ^(Before entering a trigger program the value returned by ** sqlite3_changes() function is saved. After the trigger program ** has finished, the original value is restored.)^ ** ** <li> ^(Within a trigger program each INSERT, UPDATE and DELETE ** statement sets the value returned by sqlite3_changes() ** upon completion as normal. Of course, this value will not include ** any changes performed by sub-triggers, as the sqlite3_changes() ** value will be saved and restored after each sub-trigger has run.)^ ** </ul> ** ** ^This means that if the changes() SQL function (or similar) is used ** by the first INSERT, UPDATE or DELETE statement within a trigger, it ** returns the value as set when the calling statement began executing. ** ^If it is used by the second or subsequent such statement within a trigger ** program, the value returned reflects the number of rows modified by the ** previous INSERT, UPDATE or DELETE statement within the same trigger. ** ** See also the [sqlite3_total_changes()] interface, the ** [count_changes pragma], and the [changes() SQL function]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_changes()] is running then the value returned ** is unpredictable and not meaningful. */ SQLITE_API int SQLITE_STDCALL sqlite3_changes(sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified ** ** ^This function returns the total number of rows inserted, modified or ** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed ** since the database connection was opened, including those executed as ** part of trigger programs. ^Executing any other type of SQL statement ** does not affect the value returned by sqlite3_total_changes(). ** ** ^Changes made as part of [foreign key actions] are included in the ** count, but those made as part of REPLACE constraint resolution are ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** ** See also the [sqlite3_changes()] interface, the ** [count_changes pragma], and the [total_changes() SQL function]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_total_changes()] is running then the value ** returned is unpredictable and not meaningful. */ SQLITE_API int SQLITE_STDCALL sqlite3_total_changes(sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query ** ** ^This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically ** called in response to a user action such as pressing "Cancel" |
︙ | ︙ | |||
1936 1937 1938 1939 1940 1941 1942 | ** ^A call to sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the sqlite3_interrupt() call returns. ** ** If the database connection closes while [sqlite3_interrupt()] ** is running then bad things will likely happen. */ | | | 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 | ** ^A call to sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the sqlite3_interrupt() call returns. ** ** If the database connection closes while [sqlite3_interrupt()] ** is running then bad things will likely happen. */ SQLITE_API void SQLITE_STDCALL sqlite3_interrupt(sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete ** ** These routines are useful during command-line input to determine if the ** currently entered text seems to form a complete SQL statement or ** if additional input is needed before sending the text into |
︙ | ︙ | |||
1971 1972 1973 1974 1975 1976 1977 | ** ** The input to [sqlite3_complete()] must be a zero-terminated ** UTF-8 string. ** ** The input to [sqlite3_complete16()] must be a zero-terminated ** UTF-16 string in native byte order. */ | | | > | > | > | > > | | | > | | > < < < < < < < < < < < < < < < > | | > | | | > > | | 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 | ** ** The input to [sqlite3_complete()] must be a zero-terminated ** UTF-8 string. ** ** The input to [sqlite3_complete16()] must be a zero-terminated ** UTF-16 string in native byte order. */ SQLITE_API int SQLITE_STDCALL sqlite3_complete(const char *sql); SQLITE_API int SQLITE_STDCALL sqlite3_complete16(const void *sql); /* ** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors ** KEYWORDS: {busy-handler callback} {busy handler} ** ** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X ** that might be invoked with argument P whenever ** an attempt is made to access a database table associated with ** [database connection] D when another thread ** or process has the table locked. ** The sqlite3_busy_handler() interface is used to implement ** [sqlite3_busy_timeout()] and [PRAGMA busy_timeout]. ** ** ^If the busy callback is NULL, then [SQLITE_BUSY] ** is returned immediately upon encountering the lock. ^If the busy callback ** is not NULL, then the callback might be invoked with two arguments. ** ** ^The first argument to the busy handler is a copy of the void* pointer which ** is the third argument to sqlite3_busy_handler(). ^The second argument to ** the busy handler callback is the number of times that the busy handler has ** been invoked previously for the same locking event. ^If the ** busy callback returns 0, then no additional attempts are made to ** access the database and [SQLITE_BUSY] is returned ** to the application. ** ^If the callback returns non-zero, then another attempt ** is made to access the database and the cycle repeats. ** ** The presence of a busy handler does not guarantee that it will be invoked ** when there is lock contention. ^If SQLite determines that invoking the busy ** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] ** to the application instead of invoking the ** busy handler. ** Consider a scenario where one process is holding a read lock that ** it is trying to promote to a reserved lock and ** a second process is holding a reserved lock that it is trying ** to promote to an exclusive lock. The first process cannot proceed ** because it is blocked by the second and the second process cannot ** proceed because it is blocked by the first. If both processes ** invoke the busy handlers, neither will make any progress. Therefore, ** SQLite returns [SQLITE_BUSY] for the first process, hoping that this ** will induce the first process to release its read lock and allow ** the second process to proceed. ** ** ^The default busy callback is NULL. ** ** ^(There can only be a single busy handler defined for each ** [database connection]. Setting a new busy handler clears any ** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] ** or evaluating [PRAGMA busy_timeout=N] will change the ** busy handler and thus clear any previously set busy handler. ** ** The busy callback should not take any actions which modify the ** database connection that invoked the busy handler. In other words, ** the busy handler is not reentrant. Any such actions ** result in undefined behavior. ** ** A busy handler must not close the database connection ** or [prepared statement] that invoked the busy handler. */ SQLITE_API int SQLITE_STDCALL sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); /* ** CAPI3REF: Set A Busy Timeout ** ** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps ** for a specified amount of time when a table is locked. ^The handler ** will sleep multiple times until at least "ms" milliseconds of sleeping ** have accumulated. ^After at least "ms" milliseconds of sleeping, ** the handler returns 0 which causes [sqlite3_step()] to return ** [SQLITE_BUSY]. ** ** ^Calling this routine with an argument less than or equal to zero ** turns off all busy handlers. ** ** ^(There can only be a single busy handler for a particular ** [database connection] at any given moment. If another busy handler ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ ** ** See also: [PRAGMA busy_timeout] */ SQLITE_API int SQLITE_STDCALL sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. ** |
︙ | ︙ | |||
2132 2133 2134 2135 2136 2137 2138 | ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access ** to any internal data structures of SQLite. It uses only the public ** interface defined here. As a consequence, errors that occur in the ** wrapper layer outside of the internal [sqlite3_exec()] call are not ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()]. */ | | | > > > > | 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 | ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access ** to any internal data structures of SQLite. It uses only the public ** interface defined here. As a consequence, errors that occur in the ** wrapper layer outside of the internal [sqlite3_exec()] call are not ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ int *pnRow, /* Number of result rows written here */ int *pnColumn, /* Number of result columns written here */ char **pzErrmsg /* Error msg written here */ ); SQLITE_API void SQLITE_STDCALL sqlite3_free_table(char **result); /* ** CAPI3REF: Formatted String Printing Functions ** ** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. ** These routines understand most of the common K&R formatting options, ** plus some additional non-standard formats, detailed below. ** Note that some of the more obscure formatting options from recent ** C-library standards are omitted from this implementation. ** ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their ** results into memory obtained from [sqlite3_malloc()]. ** The strings returned by these two routines should be ** released by [sqlite3_free()]. ^Both routines return a ** NULL pointer if [sqlite3_malloc()] is unable to allocate enough ** memory to hold the resulting string. |
︙ | ︙ | |||
2179 2180 2181 2182 2183 2184 2185 | ** written will be n-1 characters. ** ** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). ** ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there | | | 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 | ** written will be n-1 characters. ** ** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). ** ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there ** is are "%q", "%Q", "%w" and "%z" options. ** ** ^(The %q option works like %s in that it substitutes a nul-terminated ** string from the argument list. But %q also doubles every '\'' character. ** %q is designed for use inside a string literal.)^ By doubling each '\'' ** character it escapes that character and allows it to be inserted into ** the string. ** |
︙ | ︙ | |||
2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 | ** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText); ** sqlite3_exec(db, zSQL, 0, 0, 0); ** sqlite3_free(zSQL); ** </pre></blockquote> ** ** The code above will render a correct SQL statement in the zSQL ** variable even if the zText variable is a NULL pointer. ** ** ^(The "%z" formatting option works like "%s" but with the ** addition that after the string has been read and copied into ** the result, [sqlite3_free()] is called on the input string.)^ */ | > > > > > > | | | | > > > > | | < | | | | | | | | | > > > > > > > > > > > > > > | > | 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 | ** char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText); ** sqlite3_exec(db, zSQL, 0, 0, 0); ** sqlite3_free(zSQL); ** </pre></blockquote> ** ** The code above will render a correct SQL statement in the zSQL ** variable even if the zText variable is a NULL pointer. ** ** ^(The "%w" formatting option is like "%q" except that it expects to ** be contained within double-quotes instead of single quotes, and it ** escapes the double-quote character instead of the single-quote ** character.)^ The "%w" formatting option is intended for safely inserting ** table and column names into a constructed SQL statement. ** ** ^(The "%z" formatting option works like "%s" but with the ** addition that after the string has been read and copied into ** the result, [sqlite3_free()] is called on the input string.)^ */ SQLITE_API char *SQLITE_CDECL sqlite3_mprintf(const char*,...); SQLITE_API char *SQLITE_STDCALL sqlite3_vmprintf(const char*, va_list); SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int,char*,const char*, ...); SQLITE_API char *SQLITE_STDCALL sqlite3_vsnprintf(int,char*,const char*, va_list); /* ** CAPI3REF: Memory Allocation Subsystem ** ** The SQLite core uses these three routines for all of its own ** internal memory allocation needs. "Core" in the previous sentence ** does not include operating-system specific VFS implementation. The ** Windows VFS uses native malloc() and free() for some operations. ** ** ^The sqlite3_malloc() routine returns a pointer to a block ** of memory at least N bytes in length, where N is the parameter. ** ^If sqlite3_malloc() is unable to obtain sufficient free ** memory, it returns a NULL pointer. ^If the parameter N to ** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns ** a NULL pointer. ** ** ^The sqlite3_malloc64(N) routine works just like ** sqlite3_malloc(N) except that N is an unsigned 64-bit integer instead ** of a signed 32-bit integer. ** ** ^Calling sqlite3_free() with a pointer previously returned ** by sqlite3_malloc() or sqlite3_realloc() releases that memory so ** that it might be reused. ^The sqlite3_free() routine is ** a no-op if is called with a NULL pointer. Passing a NULL pointer ** to sqlite3_free() is harmless. After being freed, memory ** should neither be read nor written. Even reading previously freed ** memory might result in a segmentation fault or other severe error. ** Memory corruption, a segmentation fault, or other severe error ** might result if sqlite3_free() is called with a non-NULL pointer that ** was not obtained from sqlite3_malloc() or sqlite3_realloc(). ** ** ^The sqlite3_realloc(X,N) interface attempts to resize a ** prior memory allocation X to be at least N bytes. ** ^If the X parameter to sqlite3_realloc(X,N) ** is a NULL pointer then its behavior is identical to calling ** sqlite3_malloc(N). ** ^If the N parameter to sqlite3_realloc(X,N) is zero or ** negative then the behavior is exactly the same as calling ** sqlite3_free(X). ** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation ** of at least N bytes in size or NULL if insufficient memory is available. ** ^If M is the size of the prior allocation, then min(N,M) bytes ** of the prior allocation are copied into the beginning of buffer returned ** by sqlite3_realloc(X,N) and the prior allocation is freed. ** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the ** prior allocation is not freed. ** ** ^The sqlite3_realloc64(X,N) interfaces works the same as ** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead ** of a 32-bit signed integer. ** ** ^If X is a memory allocation previously obtained from sqlite3_malloc(), ** sqlite3_malloc64(), sqlite3_realloc(), or sqlite3_realloc64(), then ** sqlite3_msize(X) returns the size of that memory allocation in bytes. ** ^The value returned by sqlite3_msize(X) might be larger than the number ** of bytes requested when X was allocated. ^If X is a NULL pointer then ** sqlite3_msize(X) returns zero. If X points to something that is not ** the beginning of memory allocation, or if it points to a formerly ** valid memory allocation that has now been freed, then the behavior ** of sqlite3_msize(X) is undefined and possibly harmful. ** ** ^The memory returned by sqlite3_malloc(), sqlite3_realloc(), ** sqlite3_malloc64(), and sqlite3_realloc64() ** is always aligned to at least an 8 byte boundary, or to a ** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time ** option is used. ** ** In SQLite version 3.5.0 and 3.5.1, it was possible to define ** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in ** implementation of these routines to be omitted. That capability |
︙ | ︙ | |||
2311 2312 2313 2314 2315 2316 2317 | ** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have ** not yet been released. ** ** The application must not read or write any part of ** a block of memory after it has been released using ** [sqlite3_free()] or [sqlite3_realloc()]. */ | | > | > | > | 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 | ** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have ** not yet been released. ** ** The application must not read or write any part of ** a block of memory after it has been released using ** [sqlite3_free()] or [sqlite3_realloc()]. */ SQLITE_API void *SQLITE_STDCALL sqlite3_malloc(int); SQLITE_API void *SQLITE_STDCALL sqlite3_malloc64(sqlite3_uint64); SQLITE_API void *SQLITE_STDCALL sqlite3_realloc(void*, int); SQLITE_API void *SQLITE_STDCALL sqlite3_realloc64(void*, sqlite3_uint64); SQLITE_API void SQLITE_STDCALL sqlite3_free(void*); SQLITE_API sqlite3_uint64 SQLITE_STDCALL sqlite3_msize(void*); /* ** CAPI3REF: Memory Allocator Statistics ** ** SQLite provides these two interfaces for reporting on the status ** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] ** routines, which form the built-in memory allocation subsystem. |
︙ | ︙ | |||
2338 2339 2340 2341 2342 2343 2344 | ** ** ^The memory high-water mark is reset to the current value of ** [sqlite3_memory_used()] if and only if the parameter to ** [sqlite3_memory_highwater()] is true. ^The value returned ** by [sqlite3_memory_highwater(1)] is the high-water mark ** prior to the reset. */ | | | > | > | | > | | | 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 | ** ** ^The memory high-water mark is reset to the current value of ** [sqlite3_memory_used()] if and only if the parameter to ** [sqlite3_memory_highwater()] is true. ^The value returned ** by [sqlite3_memory_highwater(1)] is the high-water mark ** prior to the reset. */ SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_used(void); SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_highwater(int resetFlag); /* ** CAPI3REF: Pseudo-Random Number Generator ** ** SQLite contains a high-quality pseudo-random number generator (PRNG) used to ** select random [ROWID | ROWIDs] when inserting new records into a table that ** already uses the largest possible [ROWID]. The PRNG is also used for ** the build-in random() and randomblob() SQL functions. This interface allows ** applications to access the same PRNG for other purposes. ** ** ^A call to this routine stores N bytes of randomness into buffer P. ** ^The P parameter can be a NULL pointer. ** ** ^If this routine has not been previously called or if the previous ** call had N less than one or a NULL pointer for P, then the PRNG is ** seeded using randomness obtained from the xRandomness method of ** the default [sqlite3_vfs] object. ** ^If the previous call to this routine had an N of 1 or more and a ** non-NULL P then the pseudo-randomness is generated ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. */ SQLITE_API void SQLITE_STDCALL sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks ** ** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. ** ^The authorizer callback is invoked as SQL statements are being compiled |
︙ | ︙ | |||
2441 2442 2443 2444 2445 2446 2447 | ** ** ^Note that the authorizer callback is invoked only during ** [sqlite3_prepare()] or its variants. Authorization is not ** performed during statement evaluation in [sqlite3_step()], unless ** as stated in the previous paragraph, sqlite3_step() invokes ** sqlite3_prepare_v2() to reprepare a statement after a schema change. */ | | | | | 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 | ** ** ^Note that the authorizer callback is invoked only during ** [sqlite3_prepare()] or its variants. Authorization is not ** performed during statement evaluation in [sqlite3_step()], unless ** as stated in the previous paragraph, sqlite3_step() invokes ** sqlite3_prepare_v2() to reprepare a statement after a schema change. */ SQLITE_API int SQLITE_STDCALL sqlite3_set_authorizer( sqlite3*, int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pUserData ); /* ** CAPI3REF: Authorizer Return Codes ** ** The [sqlite3_set_authorizer | authorizer callback function] must ** return either [SQLITE_OK] or one of these two constants in order ** to signal SQLite whether or not the action is permitted. See the ** [sqlite3_set_authorizer | authorizer documentation] for additional ** information. ** ** Note that SQLITE_IGNORE is also used as a [conflict resolution mode] ** returned from the [sqlite3_vtab_on_conflict()] interface. */ #define SQLITE_DENY 1 /* Abort the SQL statement with an error */ #define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ /* ** CAPI3REF: Authorizer Action Codes ** |
︙ | ︙ | |||
2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 | #define SQLITE_REINDEX 27 /* Index Name NULL */ #define SQLITE_ANALYZE 28 /* Table Name NULL */ #define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ #define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ /* ** CAPI3REF: Tracing And Profiling Functions ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. ** | > | 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 | #define SQLITE_REINDEX 27 /* Index Name NULL */ #define SQLITE_ANALYZE 28 /* Table Name NULL */ #define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ #define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ #define SQLITE_RECURSIVE 33 /* NULL NULL */ /* ** CAPI3REF: Tracing And Profiling Functions ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. ** |
︙ | ︙ | |||
2544 2545 2546 2547 2548 2549 2550 | ** time is in units of nanoseconds, however the current implementation ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite ** might provide greater resolution on the profiler callback. The ** sqlite3_profile() function is considered experimental and is ** subject to change in future versions of SQLite. */ | | | | 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 | ** time is in units of nanoseconds, however the current implementation ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite ** might provide greater resolution on the profiler callback. The ** sqlite3_profile() function is considered experimental and is ** subject to change in future versions of SQLite. */ SQLITE_API void *SQLITE_STDCALL sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_API SQLITE_EXPERIMENTAL void *SQLITE_STDCALL sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to |
︙ | ︙ | |||
2579 2580 2581 2582 2583 2584 2585 | ** ** The progress handler callback must not do anything that will modify ** the database connection that invoked the progress handler. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** */ | | | | | | 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 | ** ** The progress handler callback must not do anything that will modify ** the database connection that invoked the progress handler. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** */ SQLITE_API void SQLITE_STDCALL sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection ** ** ^These routines open an SQLite database file as specified by the ** filename argument. ^The filename argument is interpreted as UTF-8 for ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte ** order for sqlite3_open16(). ^(A [database connection] handle is usually ** returned in *ppDb, even if an error occurs. The only exception is that ** if SQLite is unable to allocate memory to hold the [sqlite3] object, ** a NULL will be written into *ppDb instead of a pointer to the [sqlite3] ** object.)^ ^(If the database is opened (and/or created) successfully, then ** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The ** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain ** an English language description of the error following a failure of any ** of the sqlite3_open() routines. ** ** ^The default encoding will be UTF-8 for databases created using ** sqlite3_open() or sqlite3_open_v2(). ^The default encoding for databases ** created using sqlite3_open16() will be UTF-16 in the native byte order. ** ** Whether or not an error occurs when it is opened, resources ** associated with the [database connection] handle should be released by ** passing it to [sqlite3_close()] when it is no longer required. ** ** The sqlite3_open_v2() interface works like sqlite3_open() ** except that it accepts two additional parameters for additional control |
︙ | ︙ | |||
2687 2688 2689 2690 2691 2692 2693 | ** present, is ignored. ** ** ^SQLite uses the path component of the URI as the name of the disk file ** which contains the database. ^If the path begins with a '/' character, ** then it is interpreted as an absolute path. ^If the path does not begin ** with a '/' (meaning that the authority section is omitted from the URI) ** then the path is interpreted as a relative path. | | | > | | 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 | ** present, is ignored. ** ** ^SQLite uses the path component of the URI as the name of the disk file ** which contains the database. ^If the path begins with a '/' character, ** then it is interpreted as an absolute path. ^If the path does not begin ** with a '/' (meaning that the authority section is omitted from the URI) ** then the path is interpreted as a relative path. ** ^(On windows, the first component of an absolute path ** is a drive specification (e.g. "C:").)^ ** ** [[core URI query parameters]] ** The query component of a URI may contain parameters that are interpreted ** either by SQLite itself, or by a [VFS | custom VFS implementation]. ** SQLite and its built-in [VFSes] interpret the ** following query parameters: ** ** <ul> ** <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of ** a VFS object that provides the operating system interface that should ** be used to access the database file on disk. ^If this option is set to ** an empty string the default VFS object is used. ^Specifying an unknown ** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is |
︙ | ︙ | |||
2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 | ** "private". ^Setting it to "shared" is equivalent to setting the ** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to ** sqlite3_open_v2(). ^Setting the cache parameter to "private" is ** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. ** ^If sqlite3_open_v2() is used and the "cache" parameter is present in ** a URI filename, its value overrides any behavior requested by setting ** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. ** </ul> ** ** ^Specifying an unknown parameter in the query component of a URI is not an ** error. Future versions of SQLite might understand additional query ** parameters. See "[query parameters with special meaning to SQLite]" for ** additional information. ** | > > > > > > > > > > > > > > > > > > > > > > | 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 | ** "private". ^Setting it to "shared" is equivalent to setting the ** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to ** sqlite3_open_v2(). ^Setting the cache parameter to "private" is ** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. ** ^If sqlite3_open_v2() is used and the "cache" parameter is present in ** a URI filename, its value overrides any behavior requested by setting ** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. ** ** <li> <b>psow</b>: ^The psow parameter indicates whether or not the ** [powersafe overwrite] property does or does not apply to the ** storage media on which the database file resides. ** ** <li> <b>nolock</b>: ^The nolock parameter is a boolean query parameter ** which if set disables file locking in rollback journal modes. This ** is useful for accessing a database on a filesystem that does not ** support locking. Caution: Database corruption might result if two ** or more processes write to the same database and any one of those ** processes uses nolock=1. ** ** <li> <b>immutable</b>: ^The immutable parameter is a boolean query ** parameter that indicates that the database file is stored on ** read-only media. ^When immutable is set, SQLite assumes that the ** database file cannot be changed, even by a process with higher ** privilege, and so the database is opened read-only and all locking ** and change detection is disabled. Caution: Setting the immutable ** property on a database file that does in fact change can result ** in incorrect query results and/or [SQLITE_CORRUPT] errors. ** See also: [SQLITE_IOCAP_IMMUTABLE]. ** ** </ul> ** ** ^Specifying an unknown parameter in the query component of a URI is not an ** error. Future versions of SQLite might understand additional query ** parameters. See "[query parameters with special meaning to SQLite]" for ** additional information. ** |
︙ | ︙ | |||
2756 2757 2758 2759 2760 2761 2762 | ** C:. Note that the %20 escaping in this example is not strictly ** necessary - space characters can be used literally ** in URI filenames. ** <tr><td> file:data.db?mode=ro&cache=private <td> ** Open file "data.db" in the current directory for read-only access. ** Regardless of whether or not shared-cache mode is enabled by ** default, use a private cache. | | | > | 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 | ** C:. Note that the %20 escaping in this example is not strictly ** necessary - space characters can be used literally ** in URI filenames. ** <tr><td> file:data.db?mode=ro&cache=private <td> ** Open file "data.db" in the current directory for read-only access. ** Regardless of whether or not shared-cache mode is enabled by ** default, use a private cache. ** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td> ** Open file "/home/fred/data.db". Use the special VFS "unix-dotfile" ** that uses dot-files in place of posix advisory locking. ** <tr><td> file:data.db?mode=readonly <td> ** An error. "readonly" is not a valid option for the "mode" parameter. ** </table> ** ** ^URI hexadecimal escape sequences (%HH) are supported within the path and ** query components of a URI. A hexadecimal escape sequence consists of a ** percent sign - "%" - followed by exactly two hexadecimal digits |
︙ | ︙ | |||
2783 2784 2785 2786 2787 2788 2789 | ** ** <b>Note to Windows Runtime users:</b> The temporary directory must be set ** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various ** features that require the use of temporary files may fail. ** ** See also: [sqlite3_temp_directory] */ | | | | | 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 | ** ** <b>Note to Windows Runtime users:</b> The temporary directory must be set ** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various ** features that require the use of temporary files may fail. ** ** See also: [sqlite3_temp_directory] */ SQLITE_API int SQLITE_STDCALL sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); SQLITE_API int SQLITE_STDCALL sqlite3_open16( const void *filename, /* Database filename (UTF-16) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); SQLITE_API int SQLITE_STDCALL sqlite3_open_v2( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb, /* OUT: SQLite db handle */ int flags, /* Flags */ const char *zVfs /* Name of VFS module to use */ ); /* |
︙ | ︙ | |||
2837 2838 2839 2840 2841 2842 2843 | ** ** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and ** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and ** is not a database file pathname pointer that SQLite passed into the xOpen ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. */ | | | | > | | | | > | | 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 | ** ** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and ** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and ** is not a database file pathname pointer that SQLite passed into the xOpen ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. */ SQLITE_API const char *SQLITE_STDCALL sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API int SQLITE_STDCALL sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_uri_int64(const char*, const char*, sqlite3_int64); /* ** CAPI3REF: Error Codes And Messages ** ** ^If the most recent sqlite3_* API call associated with ** [database connection] D failed, then the sqlite3_errcode(D) interface ** returns the numeric [result code] or [extended result code] for that ** API call. ** If the most recent API call was successful, ** then the return value from sqlite3_errcode() is undefined. ** ^The sqlite3_extended_errcode() ** interface is the same except that it always returns the ** [extended result code] even when extended result codes are ** disabled. ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively. ** ^(Memory to hold the error message string is managed internally. |
︙ | ︙ | |||
2880 2881 2882 2883 2884 2885 2886 | ** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after ** all calls to the interfaces listed here are completed. ** ** If an interface fails with SQLITE_MISUSE, that means the interface ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. */ | | | | | | | 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 | ** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after ** all calls to the interfaces listed here are completed. ** ** If an interface fails with SQLITE_MISUSE, that means the interface ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. */ SQLITE_API int SQLITE_STDCALL sqlite3_errcode(sqlite3 *db); SQLITE_API int SQLITE_STDCALL sqlite3_extended_errcode(sqlite3 *db); SQLITE_API const char *SQLITE_STDCALL sqlite3_errmsg(sqlite3*); SQLITE_API const void *SQLITE_STDCALL sqlite3_errmsg16(sqlite3*); SQLITE_API const char *SQLITE_STDCALL sqlite3_errstr(int); /* ** CAPI3REF: SQL Statement Object ** KEYWORDS: {prepared statement} {prepared statements} ** ** An instance of this object represents a single SQL statement. ** This object is variously known as a "prepared statement" or a |
︙ | ︙ | |||
2951 2952 2953 2954 2955 2956 2957 | ** attack. Developers might also want to use the [sqlite3_set_authorizer()] ** interface to further control untrusted SQL. The size of the database ** created by an untrusted script can be contained using the ** [max_page_count] [PRAGMA]. ** ** New run-time limit categories may be added in future releases. */ | | | 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 | ** attack. Developers might also want to use the [sqlite3_set_authorizer()] ** interface to further control untrusted SQL. The size of the database ** created by an untrusted script can be contained using the ** [max_page_count] [PRAGMA]. ** ** New run-time limit categories may be added in future releases. */ SQLITE_API int SQLITE_STDCALL sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Run-Time Limit Categories ** KEYWORDS: {limit category} {*limit categories} ** ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. |
︙ | ︙ | |||
3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 | ** ** [[SQLITE_LIMIT_VARIABLE_NUMBER]] ** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt> ** <dd>The maximum index number of any [parameter] in an SQL statement.)^ ** ** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt> ** <dd>The maximum depth of recursion for triggers.</dd>)^ ** </dl> */ #define SQLITE_LIMIT_LENGTH 0 #define SQLITE_LIMIT_SQL_LENGTH 1 #define SQLITE_LIMIT_COLUMN 2 #define SQLITE_LIMIT_EXPR_DEPTH 3 #define SQLITE_LIMIT_COMPOUND_SELECT 4 #define SQLITE_LIMIT_VDBE_OP 5 #define SQLITE_LIMIT_FUNCTION_ARG 6 #define SQLITE_LIMIT_ATTACHED 7 #define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} ** ** To execute an SQL query, it must first be compiled into a byte-code ** program using one of these routines. ** ** The first argument, "db", is a [database connection] obtained from a ** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or ** [sqlite3_open16()]. The database connection must not have been closed. ** ** The second argument, "zSql", is the statement to be compiled, encoded ** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2() ** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() ** use UTF-16. ** | > > > > > | | | | < | | | | < | 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 | ** ** [[SQLITE_LIMIT_VARIABLE_NUMBER]] ** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt> ** <dd>The maximum index number of any [parameter] in an SQL statement.)^ ** ** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt> ** <dd>The maximum depth of recursion for triggers.</dd>)^ ** ** [[SQLITE_LIMIT_WORKER_THREADS]] ^(<dt>SQLITE_LIMIT_WORKER_THREADS</dt> ** <dd>The maximum number of auxiliary worker threads that a single ** [prepared statement] may start.</dd>)^ ** </dl> */ #define SQLITE_LIMIT_LENGTH 0 #define SQLITE_LIMIT_SQL_LENGTH 1 #define SQLITE_LIMIT_COLUMN 2 #define SQLITE_LIMIT_EXPR_DEPTH 3 #define SQLITE_LIMIT_COMPOUND_SELECT 4 #define SQLITE_LIMIT_VDBE_OP 5 #define SQLITE_LIMIT_FUNCTION_ARG 6 #define SQLITE_LIMIT_ATTACHED 7 #define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 #define SQLITE_LIMIT_WORKER_THREADS 11 /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} ** ** To execute an SQL query, it must first be compiled into a byte-code ** program using one of these routines. ** ** The first argument, "db", is a [database connection] obtained from a ** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or ** [sqlite3_open16()]. The database connection must not have been closed. ** ** The second argument, "zSql", is the statement to be compiled, encoded ** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2() ** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() ** use UTF-16. ** ** ^If the nByte argument is negative, then zSql is read up to the ** first zero terminator. ^If nByte is positive, then it is the ** number of bytes read from zSql. ^If nByte is zero, then no prepared ** statement is generated. ** If the caller knows that the supplied string is nul-terminated, then ** there is a small performance advantage to passing an nByte parameter that ** is the number of bytes in the input string <i>including</i> ** the nul-terminator. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only ** compile the first statement in zSql, so *pzTail is left pointing to ** what remains uncompiled. ** ** ^*ppStmt is left pointing to a compiled [prepared statement] that can be |
︙ | ︙ | |||
3095 3096 3097 3098 3099 3100 3101 | ** then the statement will be automatically recompiled, as if there had been ** a schema change, on the first [sqlite3_step()] call following any change ** to the [sqlite3_bind_text | bindings] of that [parameter]. ** ^The specific value of WHERE-clause [parameter] might influence the ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. | < | | | | | | 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 | ** then the statement will be automatically recompiled, as if there had been ** a schema change, on the first [sqlite3_step()] call following any change ** to the [sqlite3_bind_text | bindings] of that [parameter]. ** ^The specific value of WHERE-clause [parameter] might influence the ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. ** </li> ** </ol> */ SQLITE_API int SQLITE_STDCALL sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); SQLITE_API int SQLITE_STDCALL sqlite3_prepare_v2( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); SQLITE_API int SQLITE_STDCALL sqlite3_prepare16( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); SQLITE_API int SQLITE_STDCALL sqlite3_prepare16_v2( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); /* ** CAPI3REF: Retrieving Statement SQL ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ SQLITE_API const char *SQLITE_STDCALL sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database ** ** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if ** and only if the [prepared statement] X makes no direct changes to ** the content of the database file. |
︙ | ︙ | |||
3166 3167 3168 3169 3170 3171 3172 | ** since the statements themselves do not actually modify the database but ** rather they control the timing of when other statements modify the ** database. ^The [ATTACH] and [DETACH] statements also cause ** sqlite3_stmt_readonly() to return true since, while those statements ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. */ | | | | 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 | ** since the statements themselves do not actually modify the database but ** rather they control the timing of when other statements modify the ** database. ^The [ATTACH] and [DETACH] statements also cause ** sqlite3_stmt_readonly() to return true since, while those statements ** change the configuration of a database connection, they do not make ** changes to the content of the database files on disk. */ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the ** [prepared statement] S has been stepped at least once using ** [sqlite3_step(S)] but has not run to completion and/or has not ** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) ** interface returns false if S is a NULL pointer. If S is not a ** NULL pointer and is not a pointer to a valid [prepared statement] ** object, then the behavior is undefined and probably undesirable. ** ** This interface can be used in combination [sqlite3_next_stmt()] ** to locate all prepared statements associated with a database ** connection that are in need of being reset. This can be used, ** for example, in diagnostic routines to search for prepared ** statements that are holding a transaction open. */ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_busy(sqlite3_stmt*); /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values ** that can be stored in a database table. SQLite uses dynamic typing |
︙ | ︙ | |||
3290 3291 3292 3293 3294 3295 3296 | ** number of <u>bytes</u> in the value, not the number of characters.)^ ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() ** is negative, then the length of the string is ** the number of bytes up to the first zero terminator. ** If the fourth parameter to sqlite3_bind_blob() is negative, then ** the behavior is undefined. ** If a non-negative fourth parameter is provided to sqlite3_bind_text() | > | | | | < > > > > > > > > | 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 | ** number of <u>bytes</u> in the value, not the number of characters.)^ ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() ** is negative, then the length of the string is ** the number of bytes up to the first zero terminator. ** If the fourth parameter to sqlite3_bind_blob() is negative, then ** the behavior is undefined. ** If a non-negative fourth parameter is provided to sqlite3_bind_text() ** or sqlite3_bind_text16() or sqlite3_bind_text64() then ** that parameter must be the byte offset ** where the NUL terminator would occur assuming the string were NUL ** terminated. If any NUL characters occur at byte offsets less than ** the value of the fourth parameter then the resulting string value will ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** ** ^The fifth argument to the BLOB and string binding interfaces ** is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called ** to dispose of the BLOB or string even if the call to bind API fails. ** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite3_bind_*() routine returns. ** ** ^The sixth argument to sqlite3_bind_text64() must be one of ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] ** to specify the encoding of the text in the third parameter. If ** the sixth argument to sqlite3_bind_text64() is not one of the ** allowed values shown above, or if the text encoding is different ** from the encoding specified by the sixth parameter, then the behavior ** is undefined. ** ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that ** is filled with zeroes. ^A zeroblob uses a fixed amount of memory ** (just an integer to hold its size) while it is being processed. ** Zeroblobs are intended to serve as placeholders for BLOBs whose ** content is later written using ** [sqlite3_blob_open | incremental BLOB I/O] routines. |
︙ | ︙ | |||
3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 | ** result is undefined and probably harmful. ** ** ^Bindings are not cleared by the [sqlite3_reset()] routine. ** ^Unbound parameters are interpreted as NULL. ** ** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an ** [error code] if anything goes wrong. ** ^[SQLITE_RANGE] is returned if the parameter ** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. ** ** See also: [sqlite3_bind_parameter_count()], ** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. */ | > > > | > > | | | | | | > > | | | | 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 | ** result is undefined and probably harmful. ** ** ^Bindings are not cleared by the [sqlite3_reset()] routine. ** ^Unbound parameters are interpreted as NULL. ** ** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an ** [error code] if anything goes wrong. ** ^[SQLITE_TOOBIG] might be returned if the size of a string or BLOB ** exceeds limits imposed by [sqlite3_limit]([SQLITE_LIMIT_LENGTH]) or ** [SQLITE_MAX_LENGTH]. ** ^[SQLITE_RANGE] is returned if the parameter ** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. ** ** See also: [sqlite3_bind_parameter_count()], ** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64, void(*)(void*)); SQLITE_API int SQLITE_STDCALL sqlite3_bind_double(sqlite3_stmt*, int, double); SQLITE_API int SQLITE_STDCALL sqlite3_bind_int(sqlite3_stmt*, int, int); SQLITE_API int SQLITE_STDCALL sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); SQLITE_API int SQLITE_STDCALL sqlite3_bind_null(sqlite3_stmt*, int); SQLITE_API int SQLITE_STDCALL sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); SQLITE_API int SQLITE_STDCALL sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); SQLITE_API int SQLITE_STDCALL sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64, void(*)(void*), unsigned char encoding); SQLITE_API int SQLITE_STDCALL sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); /* ** CAPI3REF: Number Of SQL Parameters ** ** ^This routine can be used to find the number of [SQL parameters] ** in a [prepared statement]. SQL parameters are tokens of the ** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as ** placeholders for values that are [sqlite3_bind_blob | bound] ** to the parameters at a later time. ** ** ^(This routine actually returns the index of the largest (rightmost) ** parameter. For all forms except ?NNN, this will correspond to the ** number of unique parameters. If parameters of the ?NNN form are used, ** there may be gaps in the list.)^ ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_name()], and ** [sqlite3_bind_parameter_index()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_count(sqlite3_stmt*); /* ** CAPI3REF: Name Of A Host Parameter ** ** ^The sqlite3_bind_parameter_name(P,N) interface returns ** the name of the N-th [SQL parameter] in the [prepared statement] P. ** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" |
︙ | ︙ | |||
3390 3391 3392 3393 3394 3395 3396 | ** originally specified as UTF-16 in [sqlite3_prepare16()] or ** [sqlite3_prepare16_v2()]. ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ | | | | | | 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 | ** originally specified as UTF-16 in [sqlite3_prepare16()] or ** [sqlite3_prepare16_v2()]. ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ SQLITE_API const char *SQLITE_STDCALL sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* ** CAPI3REF: Index Of A Parameter With A Given Name ** ** ^Return the index of an SQL parameter given its name. ^The ** index value returned is suitable for use as the second ** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero ** is returned if no matching parameter is found. ^The parameter ** name must be given in UTF-8 even if the original statement ** was prepared from UTF-16 text using [sqlite3_prepare16_v2()]. ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); /* ** CAPI3REF: Reset All Bindings On A Prepared Statement ** ** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. ** ^Use this routine to reset all host parameters to NULL. */ SQLITE_API int SQLITE_STDCALL sqlite3_clear_bindings(sqlite3_stmt*); /* ** CAPI3REF: Number Of Columns In A Result Set ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). ** ** See also: [sqlite3_data_count()] */ SQLITE_API int SQLITE_STDCALL sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set ** ** ^These routines return the name assigned to a particular column ** in the result set of a [SELECT] statement. ^The sqlite3_column_name() ** interface returns a pointer to a zero-terminated UTF-8 string |
︙ | ︙ | |||
3454 3455 3456 3457 3458 3459 3460 | ** NULL pointer is returned. ** ** ^The name of a result column is the value of the "AS" clause for ** that column, if there is an AS clause. If there is no AS clause ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. */ | | | | 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 | ** NULL pointer is returned. ** ** ^The name of a result column is the value of the "AS" clause for ** that column, if there is an AS clause. If there is no AS clause ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. */ SQLITE_API const char *SQLITE_STDCALL sqlite3_column_name(sqlite3_stmt*, int N); SQLITE_API const void *SQLITE_STDCALL sqlite3_column_name16(sqlite3_stmt*, int N); /* ** CAPI3REF: Source Of Data In A Query Result ** ** ^These routines provide a means to determine the database, table, and ** table column that is the origin of a particular result column in ** [SELECT] statement. |
︙ | ︙ | |||
3502 3503 3504 3505 3506 3507 3508 | ** undefined. ** ** If two or more threads call one or more ** [sqlite3_column_database_name | column metadata interfaces] ** for the same [prepared statement] and result column ** at the same time then the results are undefined. */ | | | | | | | | 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 | ** undefined. ** ** If two or more threads call one or more ** [sqlite3_column_database_name | column metadata interfaces] ** for the same [prepared statement] and result column ** at the same time then the results are undefined. */ SQLITE_API const char *SQLITE_STDCALL sqlite3_column_database_name(sqlite3_stmt*,int); SQLITE_API const void *SQLITE_STDCALL sqlite3_column_database_name16(sqlite3_stmt*,int); SQLITE_API const char *SQLITE_STDCALL sqlite3_column_table_name(sqlite3_stmt*,int); SQLITE_API const void *SQLITE_STDCALL sqlite3_column_table_name16(sqlite3_stmt*,int); SQLITE_API const char *SQLITE_STDCALL sqlite3_column_origin_name(sqlite3_stmt*,int); SQLITE_API const void *SQLITE_STDCALL sqlite3_column_origin_name16(sqlite3_stmt*,int); /* ** CAPI3REF: Declared Datatype Of A Query Result ** ** ^(The first parameter is a [prepared statement]. ** If this statement is a [SELECT] statement and the Nth column of the ** returned result set of that [SELECT] is a table column (not an |
︙ | ︙ | |||
3538 3539 3540 3541 3542 3543 3544 | ** ^SQLite uses dynamic run-time typing. ^So just because a column ** is declared to contain a particular type does not mean that the ** data stored in that column is of the declared type. SQLite is ** strongly typed, but the typing is dynamic not static. ^Type ** is associated with individual values, not with the containers ** used to hold those values. */ | | | | 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 | ** ^SQLite uses dynamic run-time typing. ^So just because a column ** is declared to contain a particular type does not mean that the ** data stored in that column is of the declared type. SQLite is ** strongly typed, but the typing is dynamic not static. ^Type ** is associated with individual values, not with the containers ** used to hold those values. */ SQLITE_API const char *SQLITE_STDCALL sqlite3_column_decltype(sqlite3_stmt*,int); SQLITE_API const void *SQLITE_STDCALL sqlite3_column_decltype16(sqlite3_stmt*,int); /* ** CAPI3REF: Evaluate An SQL Statement ** ** After a [prepared statement] has been prepared using either ** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy ** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function |
︙ | ︙ | |||
3618 3619 3620 3621 3622 3623 3624 | ** We admit that this is a goofy design. The problem has been fixed ** with the "v2" interface. If you prepare all of your SQL statements ** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead ** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. */ | | | | 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 | ** We admit that this is a goofy design. The problem has been fixed ** with the "v2" interface. If you prepare all of your SQL statements ** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead ** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. */ SQLITE_API int SQLITE_STDCALL sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** ** ^The sqlite3_data_count(P) interface returns the number of columns in the ** current row of the result set of [prepared statement] P. ** ^If prepared statement P does not have results ready to return ** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of ** interfaces) then sqlite3_data_count(P) returns 0. ** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. ** ^The sqlite3_data_count(P) routine returns 0 if the previous call to ** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) ** will return non-zero if previous call to [sqlite3_step](P) returned ** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] ** where it always returns zero since each step of that multi-step ** pragma returns 0 columns of data. ** ** See also: [sqlite3_column_count()] */ SQLITE_API int SQLITE_STDCALL sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Fundamental Datatypes ** KEYWORDS: SQLITE_TEXT ** ** ^(Every value in SQLite has one of five fundamental datatypes: ** |
︙ | ︙ | |||
3757 3758 3759 3760 3761 3762 3763 | ** ** <blockquote> ** <table border="1"> ** <tr><th> Internal<br>Type <th> Requested<br>Type <th> Conversion ** ** <tr><td> NULL <td> INTEGER <td> Result is 0 ** <tr><td> NULL <td> FLOAT <td> Result is 0.0 | | | | | | | | | | 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 | ** ** <blockquote> ** <table border="1"> ** <tr><th> Internal<br>Type <th> Requested<br>Type <th> Conversion ** ** <tr><td> NULL <td> INTEGER <td> Result is 0 ** <tr><td> NULL <td> FLOAT <td> Result is 0.0 ** <tr><td> NULL <td> TEXT <td> Result is a NULL pointer ** <tr><td> NULL <td> BLOB <td> Result is a NULL pointer ** <tr><td> INTEGER <td> FLOAT <td> Convert from integer to float ** <tr><td> INTEGER <td> TEXT <td> ASCII rendering of the integer ** <tr><td> INTEGER <td> BLOB <td> Same as INTEGER->TEXT ** <tr><td> FLOAT <td> INTEGER <td> [CAST] to INTEGER ** <tr><td> FLOAT <td> TEXT <td> ASCII rendering of the float ** <tr><td> FLOAT <td> BLOB <td> [CAST] to BLOB ** <tr><td> TEXT <td> INTEGER <td> [CAST] to INTEGER ** <tr><td> TEXT <td> FLOAT <td> [CAST] to REAL ** <tr><td> TEXT <td> BLOB <td> No change ** <tr><td> BLOB <td> INTEGER <td> [CAST] to INTEGER ** <tr><td> BLOB <td> FLOAT <td> [CAST] to REAL ** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed ** </table> ** </blockquote>)^ ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its ** own equivalent internal routines. The atoi() and atof() names are |
︙ | ︙ | |||
3825 3826 3827 3828 3829 3830 3831 | ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() ** with calls to sqlite3_column_bytes(). ** ** ^The pointers returned are valid until a type conversion occurs as ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned | | | | | | | | | | | | | 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 | ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() ** with calls to sqlite3_column_bytes(). ** ** ^The pointers returned are valid until a type conversion occurs as ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned ** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** ** ^(If a memory allocation error occurs during the evaluation of any ** of these routines, a default value is returned. The default value ** is either the integer 0, the floating point number 0.0, or a NULL ** pointer. Subsequent calls to [sqlite3_errcode()] will return ** [SQLITE_NOMEM].)^ */ SQLITE_API const void *SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt*, int iCol); SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt*, int iCol); SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt*, int iCol); SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt*, int iCol); SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt*, int iCol); SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt*, int iCol); SQLITE_API const void *SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt*, int iCol); SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors ** or if the statement is never been evaluated, then sqlite3_finalize() returns |
︙ | ︙ | |||
3870 3871 3872 3873 3874 3875 3876 | ** ** The application must finalize every [prepared statement] in order to avoid ** resource leaks. It is a grievous error for the application to try to use ** a prepared statement after it has been finalized. Any use of a prepared ** statement after it has been finalized can result in undefined and ** undesirable behavior such as segfaults and heap corruption. */ | | | 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 | ** ** The application must finalize every [prepared statement] in order to avoid ** resource leaks. It is a grievous error for the application to try to use ** a prepared statement after it has been finalized. Any use of a prepared ** statement after it has been finalized can result in undefined and ** undesirable behavior such as segfaults and heap corruption. */ SQLITE_API int SQLITE_STDCALL sqlite3_finalize(sqlite3_stmt *pStmt); /* ** CAPI3REF: Reset A Prepared Statement Object ** ** The sqlite3_reset() function is called to reset a [prepared statement] ** object back to its initial state, ready to be re-executed. ** ^Any SQL statement variables that had values bound to them using |
︙ | ︙ | |||
3896 3897 3898 3899 3900 3901 3902 | ** ^If the most recent call to [sqlite3_step(S)] for the ** [prepared statement] S indicated an error, then ** [sqlite3_reset(S)] returns an appropriate [error code]. ** ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ | | | 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 | ** ^If the most recent call to [sqlite3_step(S)] for the ** [prepared statement] S indicated an error, then ** [sqlite3_reset(S)] returns an appropriate [error code]. ** ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ SQLITE_API int SQLITE_STDCALL sqlite3_reset(sqlite3_stmt *pStmt); /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** |
︙ | ︙ | |||
3934 3935 3936 3937 3938 3939 3940 | ** aggregate may take any number of arguments between 0 and the limit ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third ** parameter is less than -1 or greater than 127 then the behavior is ** undefined. ** ** ^The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for | > | > | < | > | > < > | > > > > > > | 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 | ** aggregate may take any number of arguments between 0 and the limit ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third ** parameter is less than -1 or greater than 127 then the behavior is ** undefined. ** ** ^The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for ** its parameters. The application should set this parameter to ** [SQLITE_UTF16LE] if the function implementation invokes ** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the ** implementation invokes [sqlite3_value_text16be()] on an input, or ** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8] ** otherwise. ^The same SQL function may be registered multiple times using ** different preferred text encodings, with different implementations for ** each encoding. ** ^When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. ** ** ^The fourth parameter may optionally be ORed with [SQLITE_DETERMINISTIC] ** to signal that the function will always return the same result given ** the same inputs within a single SQL statement. Most SQL functions are ** deterministic. The built-in [random()] SQL function is an example of a ** function that is not deterministic. The SQLite query planner is able to ** perform additional optimizations on deterministic functions, so use ** of the [SQLITE_DETERMINISTIC] flag is recommended where possible. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** ** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc |
︙ | ︙ | |||
3986 3987 3988 3989 3990 3991 3992 | ** ^Built-in functions may be overloaded by new application-defined functions. ** ** ^An application-defined function is permitted to call other ** SQLite interfaces. However, such calls must not ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ | | | | | | | | > > > > > > > > > > | | | | | | | | | 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 | ** ^Built-in functions may be overloaded by new application-defined functions. ** ** ^An application-defined function is permitted to call other ** SQLite interfaces. However, such calls must not ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ SQLITE_API int SQLITE_STDCALL sqlite3_create_function( sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); SQLITE_API int SQLITE_STDCALL sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); SQLITE_API int SQLITE_STDCALL sqlite3_create_function_v2( sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void(*xDestroy)(void*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. */ #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ #define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ #define SQLITE_UTF16 4 /* Use native byte order */ #define SQLITE_ANY 5 /* Deprecated */ #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ /* ** CAPI3REF: Function Flags ** ** These constants may be ORed together with the ** [SQLITE_UTF8 | preferred text encoding] as the fourth argument ** to [sqlite3_create_function()], [sqlite3_create_function16()], or ** [sqlite3_create_function_v2()]. */ #define SQLITE_DETERMINISTIC 0x800 /* ** CAPI3REF: Deprecated Functions ** DEPRECATED ** ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue ** to be supported. However, new applications should avoid ** the use of these functions. To encourage programmers to avoid ** these functions, we will not explain what they do. */ #ifndef SQLITE_OMIT_DEPRECATED SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_aggregate_count(sqlite3_context*); SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_expired(sqlite3_stmt*); SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_global_recover(void); SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_thread_cleanup(void); SQLITE_API SQLITE_DEPRECATED int SQLITE_STDCALL sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), void*,sqlite3_int64); #endif /* ** CAPI3REF: Obtaining SQL Function Parameter Values ** ** The C-language implementation of SQL functions and aggregates uses |
︙ | ︙ | |||
4071 4072 4073 4074 4075 4076 4077 | ** extract values from the [sqlite3_value] objects. ** ** These routines work only with [protected sqlite3_value] objects. ** Any attempt to use these routines on an [unprotected sqlite3_value] ** object results in undefined behavior. ** ** ^These routines work just like the corresponding [column access functions] | | | 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 | ** extract values from the [sqlite3_value] objects. ** ** These routines work only with [protected sqlite3_value] objects. ** Any attempt to use these routines on an [unprotected sqlite3_value] ** object results in undefined behavior. ** ** ^These routines work just like the corresponding [column access functions] ** except that these routines take a single [protected sqlite3_value] object ** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. ** ** ^The sqlite3_value_text16() interface extracts a UTF-16 string ** in the native byte-order of the host machine. ^The ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces ** extract UTF-16 strings as big-endian and little-endian respectively. ** |
︙ | ︙ | |||
4096 4097 4098 4099 4100 4101 4102 | ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], ** or [sqlite3_value_text16()]. ** ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. */ | | | | | | | | | | | | | | 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 | ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], ** or [sqlite3_value_text16()]. ** ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. */ SQLITE_API const void *SQLITE_STDCALL sqlite3_value_blob(sqlite3_value*); SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes(sqlite3_value*); SQLITE_API int SQLITE_STDCALL sqlite3_value_bytes16(sqlite3_value*); SQLITE_API double SQLITE_STDCALL sqlite3_value_double(sqlite3_value*); SQLITE_API int SQLITE_STDCALL sqlite3_value_int(sqlite3_value*); SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_value_int64(sqlite3_value*); SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_value_text(sqlite3_value*); SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16(sqlite3_value*); SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16le(sqlite3_value*); SQLITE_API const void *SQLITE_STDCALL sqlite3_value_text16be(sqlite3_value*); SQLITE_API int SQLITE_STDCALL sqlite3_value_type(sqlite3_value*); SQLITE_API int SQLITE_STDCALL sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** ** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. ** |
︙ | ︙ | |||
4151 4152 4153 4154 4155 4156 4157 | ** [sqlite3_context | SQL function context] that is the first parameter ** to the xStep or xFinal callback routine that implements the aggregate ** function. ** ** This routine must be called from the same thread in which ** the aggregate SQL function is running. */ | | | | | 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 | ** [sqlite3_context | SQL function context] that is the first parameter ** to the xStep or xFinal callback routine that implements the aggregate ** function. ** ** This routine must be called from the same thread in which ** the aggregate SQL function is running. */ SQLITE_API void *SQLITE_STDCALL sqlite3_aggregate_context(sqlite3_context*, int nBytes); /* ** CAPI3REF: User Data For Functions ** ** ^The sqlite3_user_data() interface returns a copy of ** the pointer that was the pUserData parameter (the 5th parameter) ** of the [sqlite3_create_function()] ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. ** ** This routine must be called from the same thread in which ** the application-defined function is running. */ SQLITE_API void *SQLITE_STDCALL sqlite3_user_data(sqlite3_context*); /* ** CAPI3REF: Database Connection For Functions ** ** ^The sqlite3_context_db_handle() interface returns a copy of ** the pointer to the [database connection] (the 1st parameter) ** of the [sqlite3_create_function()] ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. */ SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_context_db_handle(sqlite3_context*); /* ** CAPI3REF: Function Auxiliary Data ** ** These functions may be used by (non-aggregate) SQL functions to ** associate metadata with argument values. If the same value is passed to ** multiple invocations of the same SQL function during query execution, under |
︙ | ︙ | |||
4228 4229 4230 4231 4232 4233 4234 | ** ^(In practice, metadata is preserved between function calls for ** function parameters that are compile-time constants, including literal ** values and [parameters] and expressions composed from the same.)^ ** ** These routines must be called from the same thread in which ** the SQL function is running. */ | | | | 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 | ** ^(In practice, metadata is preserved between function calls for ** function parameters that are compile-time constants, including literal ** values and [parameters] and expressions composed from the same.)^ ** ** These routines must be called from the same thread in which ** the SQL function is running. */ SQLITE_API void *SQLITE_STDCALL sqlite3_get_auxdata(sqlite3_context*, int N); SQLITE_API void SQLITE_STDCALL sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* ** CAPI3REF: Constants Defining Special Destructor Behavior ** ** These are special values for the destructor that is passed in as the ** final argument to routines like [sqlite3_result_blob()]. ^If the destructor |
︙ | ︙ | |||
4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 | ** of the application-defined function to be NULL. ** ** ^The sqlite3_result_text(), sqlite3_result_text16(), ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces ** set the return value of the application-defined function to be ** a text string which is represented as UTF-8, UTF-16 native byte order, ** UTF-16 little endian, or UTF-16 big endian, respectively. ** ^SQLite takes the text result from the application from ** the 2nd parameter of the sqlite3_result_text* interfaces. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is negative, then SQLite takes result text from the 2nd parameter ** through the first zero character. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text | > > > > | 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 | ** of the application-defined function to be NULL. ** ** ^The sqlite3_result_text(), sqlite3_result_text16(), ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces ** set the return value of the application-defined function to be ** a text string which is represented as UTF-8, UTF-16 native byte order, ** UTF-16 little endian, or UTF-16 big endian, respectively. ** ^The sqlite3_result_text64() interface sets the return value of an ** application-defined function to be a text string in an encoding ** specified by the fifth (and last) parameter, which must be one ** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. ** ^SQLite takes the text result from the application from ** the 2nd parameter of the sqlite3_result_text* interfaces. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is negative, then SQLite takes result text from the 2nd parameter ** through the first zero character. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text |
︙ | ︙ | |||
4360 4361 4362 4363 4364 4365 4366 | ** [unprotected sqlite3_value] object is required, so either ** kind of [sqlite3_value] object can be used with this interface. ** ** If these routines are called from within the different thread ** than the one containing the application-defined function that received ** the [sqlite3_context] pointer, the results are undefined. */ | | > > | | | | | | | | | | > > | | | | | | 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 | ** [unprotected sqlite3_value] object is required, so either ** kind of [sqlite3_value] object can be used with this interface. ** ** If these routines are called from within the different thread ** than the one containing the application-defined function that received ** the [sqlite3_context] pointer, the results are undefined. */ SQLITE_API void SQLITE_STDCALL sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); SQLITE_API void SQLITE_STDCALL sqlite3_result_blob64(sqlite3_context*,const void*, sqlite3_uint64,void(*)(void*)); SQLITE_API void SQLITE_STDCALL sqlite3_result_double(sqlite3_context*, double); SQLITE_API void SQLITE_STDCALL sqlite3_result_error(sqlite3_context*, const char*, int); SQLITE_API void SQLITE_STDCALL sqlite3_result_error16(sqlite3_context*, const void*, int); SQLITE_API void SQLITE_STDCALL sqlite3_result_error_toobig(sqlite3_context*); SQLITE_API void SQLITE_STDCALL sqlite3_result_error_nomem(sqlite3_context*); SQLITE_API void SQLITE_STDCALL sqlite3_result_error_code(sqlite3_context*, int); SQLITE_API void SQLITE_STDCALL sqlite3_result_int(sqlite3_context*, int); SQLITE_API void SQLITE_STDCALL sqlite3_result_int64(sqlite3_context*, sqlite3_int64); SQLITE_API void SQLITE_STDCALL sqlite3_result_null(sqlite3_context*); SQLITE_API void SQLITE_STDCALL sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); SQLITE_API void SQLITE_STDCALL sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, void(*)(void*), unsigned char encoding); SQLITE_API void SQLITE_STDCALL sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); SQLITE_API void SQLITE_STDCALL sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void SQLITE_STDCALL sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void SQLITE_STDCALL sqlite3_result_value(sqlite3_context*, sqlite3_value*); SQLITE_API void SQLITE_STDCALL sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences ** ** ^These functions add, remove, or modify a [collation] associated ** with the [database connection] specified as the first argument. ** |
︙ | ︙ | |||
4456 4457 4458 4459 4460 4461 4462 | ** themselves rather than expecting SQLite to deal with it for them. ** This is different from every other SQLite interface. The inconsistency ** is unfortunate but cannot be changed without breaking backwards ** compatibility. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ | | | | | 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 | ** themselves rather than expecting SQLite to deal with it for them. ** This is different from every other SQLite interface. The inconsistency ** is unfortunate but cannot be changed without breaking backwards ** compatibility. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); SQLITE_API int SQLITE_STDCALL sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); SQLITE_API int SQLITE_STDCALL sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); |
︙ | ︙ | |||
4505 4506 4507 4508 4509 4510 4511 | ** sequence function required. The fourth parameter is the name of the ** required collation sequence.)^ ** ** The callback function should register the desired collation using ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. */ | | | | | | | | | | 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 | ** sequence function required. The fourth parameter is the name of the ** required collation sequence.)^ ** ** The callback function should register the desired collation using ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const char*) ); SQLITE_API int SQLITE_STDCALL sqlite3_collation_needed16( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const void*) ); #ifdef SQLITE_HAS_CODEC /* ** Specify the key for an encrypted database. This routine should be ** called right after sqlite3_open(). ** ** The code to implement this API is not available in the public release ** of SQLite. */ SQLITE_API int SQLITE_STDCALL sqlite3_key( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The key */ ); SQLITE_API int SQLITE_STDCALL sqlite3_key_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The key */ ); /* ** Change the key on an open database. If the current database is not ** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the ** database is decrypted. ** ** The code to implement this API is not available in the public release ** of SQLite. */ SQLITE_API int SQLITE_STDCALL sqlite3_rekey( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ ); SQLITE_API int SQLITE_STDCALL sqlite3_rekey_v2( sqlite3 *db, /* Database to be rekeyed */ const char *zDbName, /* Name of the database */ const void *pKey, int nKey /* The new key */ ); /* ** Specify the activation key for a SEE database. Unless ** activated, none of the SEE routines will work. */ SQLITE_API void SQLITE_STDCALL sqlite3_activate_see( const char *zPassPhrase /* Activation phrase */ ); #endif #ifdef SQLITE_ENABLE_CEROD /* ** Specify the activation key for a CEROD database. Unless ** activated, none of the CEROD routines will work. */ SQLITE_API void SQLITE_STDCALL sqlite3_activate_cerod( const char *zPassPhrase /* Activation phrase */ ); #endif /* ** CAPI3REF: Suspend Execution For A Short Time ** |
︙ | ︙ | |||
4588 4589 4590 4591 4592 4593 4594 | ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. If the xSleep() method ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ | | > > > > > > > > > > > > | 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 | ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. If the xSleep() method ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ SQLITE_API int SQLITE_STDCALL sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files ** ** ^(If this global variable is made to point to a string which is ** the name of a folder (a.k.a. directory), then all temporary files ** created by SQLite when using a built-in [sqlite3_vfs | VFS] ** will be placed in that directory.)^ ^If this variable ** is a NULL pointer, then SQLite performs a search for an appropriate ** temporary file directory. ** ** Applications are strongly discouraged from using this global variable. ** It is required to set a temporary folder on Windows Runtime (WinRT). ** But for all other platforms, it is highly recommended that applications ** neither read nor write this variable. This global variable is a relic ** that exists for backwards compatibility of legacy applications and should ** be avoided in new projects. ** ** It is not safe to read or modify this variable in more than one ** thread at a time. It is not safe to read or modify this variable ** if a [database connection] is being used at the same time in a separate ** thread. ** It is intended that this variable be set once ** as part of process initialization and before any SQLite interface ** routines have been called and that this variable remain unchanged ** thereafter. ** ** ^The [temp_store_directory pragma] may modify this variable and cause ** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, ** the [temp_store_directory pragma] always assumes that any string ** that this variable points to is held in memory obtained from ** [sqlite3_malloc] and the pragma may attempt to free that memory ** using [sqlite3_free]. ** Hence, if this variable is modified directly, either it should be ** made NULL or made to point to memory obtained from [sqlite3_malloc] ** or else the use of the [temp_store_directory pragma] should be avoided. ** Except when requested by the [temp_store_directory pragma], SQLite ** does not free the memory that sqlite3_temp_directory points to. If ** the application wants that memory to be freed, it must do ** so itself, taking care to only do so after all [database connection] ** objects have been destroyed. ** ** <b>Note to Windows Runtime users:</b> The temporary directory must be set ** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various ** features that require the use of temporary files may fail. Here is an ** example of how to do this using C++ with the Windows Runtime: ** ** <blockquote><pre> |
︙ | ︙ | |||
4694 4695 4696 4697 4698 4699 4700 | ** find out whether SQLite automatically rolled back the transaction after ** an error is to use this function. ** ** If another thread changes the autocommit status of the database ** connection while this routine is running, then the return value ** is undefined. */ | | | | | | | 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 | ** find out whether SQLite automatically rolled back the transaction after ** an error is to use this function. ** ** If another thread changes the autocommit status of the database ** connection while this routine is running, then the return value ** is undefined. */ SQLITE_API int SQLITE_STDCALL sqlite3_get_autocommit(sqlite3*); /* ** CAPI3REF: Find The Database Handle Of A Prepared Statement ** ** ^The sqlite3_db_handle interface returns the [database connection] handle ** to which a [prepared statement] belongs. ^The [database connection] ** returned by sqlite3_db_handle is the same [database connection] ** that was the first argument ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ SQLITE_API sqlite3 *SQLITE_STDCALL sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Return The Filename For A Database Connection ** ** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename ** associated with database N of connection D. ^The main database file ** has the name "main". If there is no attached database N on the database ** connection D, or if database N is a temporary or in-memory database, then ** a NULL pointer is returned. ** ** ^The filename returned by this function is the output of the ** xFullPathname method of the [VFS]. ^In other words, the filename ** will be an absolute pathname, even if the filename used ** to open the database originally was a URI or relative pathname. */ SQLITE_API const char *SQLITE_STDCALL sqlite3_db_filename(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Determine if a database is read-only ** ** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N ** of connection D is read-only, 0 if it is read/write, or -1 if N is not ** the name of a database on connection D. */ SQLITE_API int SQLITE_STDCALL sqlite3_db_readonly(sqlite3 *db, const char *zDbName); /* ** CAPI3REF: Find the next prepared statement ** ** ^This interface returns a pointer to the next [prepared statement] after ** pStmt associated with the [database connection] pDb. ^If pStmt is NULL ** then this interface returns a pointer to the first prepared statement ** associated with the database connection pDb. ^If no prepared statement ** satisfies the conditions of this routine, it returns NULL. ** ** The [database connection] pointer D in a call to ** [sqlite3_next_stmt(D,S)] must refer to an open database ** connection and in particular must not be a NULL pointer. */ SQLITE_API sqlite3_stmt *SQLITE_STDCALL sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); /* ** CAPI3REF: Commit And Rollback Notification Callbacks ** ** ^The sqlite3_commit_hook() interface registers a callback ** function to be invoked whenever a transaction is [COMMIT | committed]. ** ^Any callback set by a previous call to sqlite3_commit_hook() |
︙ | ︙ | |||
4794 4795 4796 4797 4798 4799 4800 | ** rolled back if an explicit "ROLLBACK" statement is executed, or ** an error or constraint causes an implicit rollback to occur. ** ^The rollback callback is not invoked if a transaction is ** automatically rolled back because the database connection is closed. ** ** See also the [sqlite3_update_hook()] interface. */ | | | | > | > | 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 | ** rolled back if an explicit "ROLLBACK" statement is executed, or ** an error or constraint causes an implicit rollback to occur. ** ^The rollback callback is not invoked if a transaction is ** automatically rolled back because the database connection is closed. ** ** See also the [sqlite3_update_hook()] interface. */ SQLITE_API void *SQLITE_STDCALL sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); SQLITE_API void *SQLITE_STDCALL sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); /* ** CAPI3REF: Data Change Notification Callbacks ** ** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument ** to be invoked whenever a row is updated, inserted or deleted in ** a rowid table. ** ^Any callback set by a previous call to this function ** for the same database connection is overridden. ** ** ^The second argument is a pointer to the function to invoke when a ** row is updated, inserted or deleted in a rowid table. ** ^The first argument to the callback is a copy of the third argument ** to sqlite3_update_hook(). ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], ** or [SQLITE_UPDATE], depending on the operation that caused the callback ** to be invoked. ** ^The third and fourth arguments to the callback contain pointers to the ** database and table name containing the affected row. ** ^The final callback parameter is the [rowid] of the row. ** ^In the case of an update, this is the [rowid] after the update takes place. ** ** ^(The update hook is not invoked when internal system tables are ** modified (i.e. sqlite_master and sqlite_sequence).)^ ** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified. ** ** ^In the current implementation, the update hook ** is not invoked when duplication rows are deleted because of an ** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook ** invoked when rows are deleted using the [truncate optimization]. ** The exceptions defined in this paragraph might change in a future ** release of SQLite. |
︙ | ︙ | |||
4843 4844 4845 4846 4847 4848 4849 | ** returns the P argument from the previous call ** on the same [database connection] D, or NULL for ** the first call on D. ** ** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] ** interfaces. */ | | | 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 | ** returns the P argument from the previous call ** on the same [database connection] D, or NULL for ** the first call on D. ** ** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] ** interfaces. */ SQLITE_API void *SQLITE_STDCALL sqlite3_update_hook( sqlite3*, void(*)(void *,int ,char const *,char const *,sqlite3_int64), void* ); /* ** CAPI3REF: Enable Or Disable Shared Pager Cache |
︙ | ︙ | |||
4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 | ** ** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled ** successfully. An [error code] is returned otherwise.)^ ** ** ^Shared cache is disabled by default. But this might change in ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** ** This interface is threadsafe on processors where writing a ** 32-bit integer is atomic. ** ** See Also: [SQLite Shared-Cache Mode] */ | > > > > > | | | | | | 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 | ** ** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled ** successfully. An [error code] is returned otherwise.)^ ** ** ^Shared cache is disabled by default. But this might change in ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** ** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0 ** and will always return SQLITE_MISUSE. On those systems, ** shared cache mode should be enabled per-database connection via ** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE]. ** ** This interface is threadsafe on processors where writing a ** 32-bit integer is atomic. ** ** See Also: [SQLite Shared-Cache Mode] */ SQLITE_API int SQLITE_STDCALL sqlite3_enable_shared_cache(int); /* ** CAPI3REF: Attempt To Free Heap Memory ** ** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations ** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. ** ^The sqlite3_release_memory() routine is a no-op returning zero ** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. ** ** See also: [sqlite3_db_release_memory()] */ SQLITE_API int SQLITE_STDCALL sqlite3_release_memory(int); /* ** CAPI3REF: Free Memory Used By A Database Connection ** ** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap ** memory as possible from database connection D. Unlike the ** [sqlite3_release_memory()] interface, this interface is in effect even ** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is ** omitted. ** ** See also: [sqlite3_release_memory()] */ SQLITE_API int SQLITE_STDCALL sqlite3_db_release_memory(sqlite3*); /* ** CAPI3REF: Impose A Limit On Heap Size ** ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the ** soft limit on the amount of heap memory that may be allocated by SQLite. ** ^SQLite strives to keep heap memory utilization below the soft heap |
︙ | ︙ | |||
4959 4960 4961 4962 4963 4964 4965 | ** the page cache is the predominate memory user in SQLite, most ** applications will achieve adequate soft heap limit enforcement without ** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. ** ** The circumstances under which SQLite will enforce the soft heap limit may ** changes in future releases of SQLite. */ | | | | > | > | > > > > > > | | | < | | > | | | | | < < < < | | 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 | ** the page cache is the predominate memory user in SQLite, most ** applications will achieve adequate soft heap limit enforcement without ** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. ** ** The circumstances under which SQLite will enforce the soft heap limit may ** changes in future releases of SQLite. */ SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_soft_heap_limit64(sqlite3_int64 N); /* ** CAPI3REF: Deprecated Soft Heap Limit Interface ** DEPRECATED ** ** This is a deprecated version of the [sqlite3_soft_heap_limit64()] ** interface. This routine is provided for historical compatibility ** only. All new applications should use the ** [sqlite3_soft_heap_limit64()] interface rather than this one. */ SQLITE_API SQLITE_DEPRECATED void SQLITE_STDCALL sqlite3_soft_heap_limit(int N); /* ** CAPI3REF: Extract Metadata About A Column Of A Table ** ** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns ** information about column C of table T in database D ** on [database connection] X.)^ ^The sqlite3_table_column_metadata() ** interface returns SQLITE_OK and fills in the non-NULL pointers in ** the final five arguments with appropriate values if the specified ** column exists. ^The sqlite3_table_column_metadata() interface returns ** SQLITE_ERROR and if the specified column does not exist. ** ^If the column-name parameter to sqlite3_table_column_metadata() is a ** NULL pointer, then this routine simply checks for the existance of the ** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it ** does not. ** ** ^The column is identified by the second, third and fourth parameters to ** this function. ^(The second parameter is either the name of the database ** (i.e. "main", "temp", or an attached database) containing the specified ** table or NULL.)^ ^If it is NULL, then all attached databases are searched ** for the table using the same algorithm used by the database engine to ** resolve unqualified table references. ** ** ^The third and fourth parameters to this function are the table and column ** name of the desired column, respectively. ** ** ^Metadata is returned by writing to the memory locations passed as the 5th ** and subsequent parameters to this function. ^Any of these arguments may be ** NULL, in which case the corresponding element of metadata is omitted. ** ** ^(<blockquote> ** <table border="1"> ** <tr><th> Parameter <th> Output<br>Type <th> Description ** ** <tr><td> 5th <td> const char* <td> Data type ** <tr><td> 6th <td> const char* <td> Name of default collation sequence ** <tr><td> 7th <td> int <td> True if column has a NOT NULL constraint ** <tr><td> 8th <td> int <td> True if column is part of the PRIMARY KEY ** <tr><td> 9th <td> int <td> True if column is [AUTOINCREMENT] ** </table> ** </blockquote>)^ ** ** ^The memory pointed to by the character pointers returned for the ** declaration type and collation sequence is valid until the next ** call to any SQLite API function. ** ** ^If the specified table is actually a view, an [error code] is returned. ** ** ^If the specified column is "rowid", "oid" or "_rowid_" and the table ** is not a [WITHOUT ROWID] table and an ** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output ** parameters are set for the explicitly declared column. ^(If there is no ** [INTEGER PRIMARY KEY] column, then the outputs ** for the [rowid] are set as follows: ** ** <pre> ** data type: "INTEGER" ** collation sequence: "BINARY" ** not null: 0 ** primary key: 1 ** auto increment: 0 ** </pre>)^ ** ** ^This function causes all database schemas to be read from disk and ** parsed, if that has not already been done, and returns an error if ** any errors are encountered while loading the schema. */ SQLITE_API int SQLITE_STDCALL sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ const char *zTableName, /* Table name */ const char *zColumnName, /* Column name */ char const **pzDataType, /* OUTPUT: Declared data type */ char const **pzCollSeq, /* OUTPUT: Collation sequence name */ int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ |
︙ | ︙ | |||
5081 5082 5083 5084 5085 5086 5087 | ** ** ^Extension loading must be enabled using ** [sqlite3_enable_load_extension()] prior to calling this API, ** otherwise an error will be returned. ** ** See also the [load_extension() SQL function]. */ | | | | 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 | ** ** ^Extension loading must be enabled using ** [sqlite3_enable_load_extension()] prior to calling this API, ** otherwise an error will be returned. ** ** See also the [load_extension() SQL function]. */ SQLITE_API int SQLITE_STDCALL sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ const char *zFile, /* Name of the shared library containing extension */ const char *zProc, /* Entry point. Derived from zFile if 0 */ char **pzErrMsg /* Put error message here if not 0 */ ); /* ** CAPI3REF: Enable Or Disable Extension Loading ** ** ^So as not to open security holes in older applications that are ** unprepared to deal with [extension loading], and as a means of disabling ** [extension loading] while evaluating user-entered SQL, the following API ** is provided to turn the [sqlite3_load_extension()] mechanism on and off. ** ** ^Extension loading is off by default. ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ SQLITE_API int SQLITE_STDCALL sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* ** CAPI3REF: Automatically Load Statically Linked Extensions ** ** ^This interface causes the xEntryPoint() function to be invoked for ** each new [database connection] that is created. The idea here is that ** xEntryPoint() is the entry point for a statically linked [SQLite extension] |
︙ | ︙ | |||
5139 5140 5141 5142 5143 5144 5145 | ** ^Calling sqlite3_auto_extension(X) with an entry point X that is already ** on the list of automatic extensions is a harmless no-op. ^No entry point ** will be called more than once for each database connection that is opened. ** ** See also: [sqlite3_reset_auto_extension()] ** and [sqlite3_cancel_auto_extension()] */ | | | | | 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 | ** ^Calling sqlite3_auto_extension(X) with an entry point X that is already ** on the list of automatic extensions is a harmless no-op. ^No entry point ** will be called more than once for each database connection that is opened. ** ** See also: [sqlite3_reset_auto_extension()] ** and [sqlite3_cancel_auto_extension()] */ SQLITE_API int SQLITE_STDCALL sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Cancel Automatic Extension Loading ** ** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the ** initialization routine X that was registered using a prior call to ** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)] ** routine returns 1 if initialization routine X was successfully ** unregistered and it returns 0 if X was not on the list of initialization ** routines. */ SQLITE_API int SQLITE_STDCALL sqlite3_cancel_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading ** ** ^This interface disables all automatic extensions previously ** registered using [sqlite3_auto_extension()]. */ SQLITE_API void SQLITE_STDCALL sqlite3_reset_auto_extension(void); /* ** The interface to the virtual-table mechanism is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. ** ** When the virtual-table mechanism stabilizes, we will declare the |
︙ | ︙ | |||
5277 5278 5279 5280 5281 5282 5283 | ** ^[sqlite3_free()] is used to free idxPtr if and only if ** needToFreeIdxPtr is true. ** ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in ** the correct order to satisfy the ORDER BY clause so that no separate ** sorting step is required. ** | | > | > | | > > > > > > > > > > | 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 | ** ^[sqlite3_free()] is used to free idxPtr if and only if ** needToFreeIdxPtr is true. ** ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in ** the correct order to satisfy the ORDER BY clause so that no separate ** sorting step is required. ** ** ^The estimatedCost value is an estimate of the cost of a particular ** strategy. A cost of N indicates that the cost of the strategy is similar ** to a linear scan of an SQLite table with N rows. A cost of log(N) ** indicates that the expense of the operation is similar to that of a ** binary search on a unique indexed field of an SQLite table with N rows. ** ** ^The estimatedRows value is an estimate of the number of rows that ** will be returned by the strategy. ** ** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info ** structure for SQLite version 3.8.2. If a virtual table extension is ** used with an SQLite version earlier than 3.8.2, the results of attempting ** to read or write the estimatedRows field are undefined (but are likely ** to included crashing the application). The estimatedRows field should ** therefore only be used if [sqlite3_libversion_number()] returns a ** value greater than or equal to 3008002. */ struct sqlite3_index_info { /* Inputs */ int nConstraint; /* Number of entries in aConstraint */ struct sqlite3_index_constraint { int iColumn; /* Column on left-hand side of constraint */ unsigned char op; /* Constraint operator */ |
︙ | ︙ | |||
5305 5306 5307 5308 5309 5310 5311 | int argvIndex; /* if >0, constraint is part of argv to xFilter */ unsigned char omit; /* Do not code a test for this constraint */ } *aConstraintUsage; int idxNum; /* Number used to identify the index */ char *idxStr; /* String, possibly obtained from sqlite3_malloc */ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ int orderByConsumed; /* True if output is already ordered */ | | > > | 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 | int argvIndex; /* if >0, constraint is part of argv to xFilter */ unsigned char omit; /* Do not code a test for this constraint */ } *aConstraintUsage; int idxNum; /* Number used to identify the index */ char *idxStr; /* String, possibly obtained from sqlite3_malloc */ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ int orderByConsumed; /* True if output is already ordered */ double estimatedCost; /* Estimated cost of using this index */ /* Fields below are only available in SQLite 3.8.2 and later */ sqlite3_int64 estimatedRows; /* Estimated number of rows returned */ }; /* ** CAPI3REF: Virtual Table Constraint Operator Codes ** ** These macros defined the allowed values for the ** [sqlite3_index_info].aConstraint[].op field. Each value represents |
︙ | ︙ | |||
5348 5349 5350 5351 5352 5353 5354 | ** invoke the destructor function (if it is not NULL) when SQLite ** no longer needs the pClientData pointer. ^The destructor will also ** be invoked if the call to sqlite3_create_module_v2() fails. ** ^The sqlite3_create_module() ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ | | | | 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 | ** invoke the destructor function (if it is not NULL) when SQLite ** no longer needs the pClientData pointer. ^The destructor will also ** be invoked if the call to sqlite3_create_module_v2() fails. ** ^The sqlite3_create_module() ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ SQLITE_API int SQLITE_STDCALL sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); SQLITE_API int SQLITE_STDCALL sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData, /* Client data for xCreate/xConnect */ void(*xDestroy)(void*) /* Module destructor function */ ); |
︙ | ︙ | |||
5382 5383 5384 5385 5386 5387 5388 | ** take care that any prior string is freed by a call to [sqlite3_free()] ** prior to assigning a new string to zErrMsg. ^After the error message ** is delivered up to the client application, the string will be automatically ** freed by sqlite3_free() and the zErrMsg field will be zeroed. */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ | | | 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 | ** take care that any prior string is freed by a call to [sqlite3_free()] ** prior to assigning a new string to zErrMsg. ^After the error message ** is delivered up to the client application, the string will be automatically ** freed by sqlite3_free() and the zErrMsg field will be zeroed. */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ int nRef; /* Number of open cursors */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; /* ** CAPI3REF: Virtual Table Cursor Object ** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} |
︙ | ︙ | |||
5417 5418 5419 5420 5421 5422 5423 | ** CAPI3REF: Declare The Schema Of A Virtual Table ** ** ^The [xCreate] and [xConnect] methods of a ** [virtual table module] call this interface ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ | | | | 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 | ** CAPI3REF: Declare The Schema Of A Virtual Table ** ** ^The [xCreate] and [xConnect] methods of a ** [virtual table module] call this interface ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ SQLITE_API int SQLITE_STDCALL sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* ** CAPI3REF: Overload A Function For A Virtual Table ** ** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. ** But global versions of those functions ** must exist in order to be overloaded.)^ ** ** ^(This API makes sure a global version of a function with a particular ** name and number of parameters exists. If no such function exists ** before this API is called, a new function is created.)^ ^The implementation ** of the new function always causes an exception to be thrown. So ** the new function is not good for anything by itself. Its only ** purpose is to be a placeholder function that can be overloaded ** by a [virtual table]. */ SQLITE_API int SQLITE_STDCALL sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); /* ** The interface to the virtual-table mechanism defined above (back up ** to a comment remarkably similar to this one) is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. ** |
︙ | ︙ | |||
5471 5472 5473 5474 5475 5476 5477 5478 | ** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located ** in row iRow, column zColumn, table zTable in database zDb; ** in other words, the same BLOB that would be selected by: ** ** <pre> ** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow; ** </pre>)^ ** | < < < < < < | | | | | > > > > | | > > > > > | > > > > > > > > > > > > > > > | | < < < > | | < | | 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 | ** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located ** in row iRow, column zColumn, table zTable in database zDb; ** in other words, the same BLOB that would be selected by: ** ** <pre> ** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow; ** </pre>)^ ** ** ^(Parameter zDb is not the filename that contains the database, but ** rather the symbolic name of the database. For attached databases, this is ** the name that appears after the AS keyword in the [ATTACH] statement. ** For the main database file, the database name is "main". For TEMP ** tables, the database name is "temp".)^ ** ** ^If the flags parameter is non-zero, then the BLOB is opened for read ** and write access. ^If the flags parameter is zero, the BLOB is opened for ** read-only access. ** ** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored ** in *ppBlob. Otherwise an [error code] is returned and, unless the error ** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided ** the API is not misused, it is always safe to call [sqlite3_blob_close()] ** on *ppBlob after this function it returns. ** ** This function fails with SQLITE_ERROR if any of the following are true: ** <ul> ** <li> ^(Database zDb does not exist)^, ** <li> ^(Table zTable does not exist within database zDb)^, ** <li> ^(Table zTable is a WITHOUT ROWID table)^, ** <li> ^(Column zColumn does not exist)^, ** <li> ^(Row iRow is not present in the table)^, ** <li> ^(The specified column of row iRow contains a value that is not ** a TEXT or BLOB value)^, ** <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE ** constraint and the blob is being opened for read/write access)^, ** <li> ^([foreign key constraints | Foreign key constraints] are enabled, ** column zColumn is part of a [child key] definition and the blob is ** being opened for read/write access)^. ** </ul> ** ** ^Unless it returns SQLITE_MISUSE, this function sets the ** [database connection] error code and message accessible via ** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions. ** ** ** ^(If the row that a BLOB handle points to is modified by an ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects ** then the BLOB handle is marked as "expired". ** This is true if any column of the row is changed, even a column ** other than the one the BLOB handle is open on.)^ ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for ** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. ** ^(Changes written into a BLOB prior to the BLOB expiring are not ** rolled back by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion.)^ ** ** ^Use the [sqlite3_blob_bytes()] interface to determine the size of ** the opened blob. ^The size of a blob may not be changed by this ** interface. Use the [UPDATE] SQL command to change the size of a ** blob. ** ** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces ** and the built-in [zeroblob] SQL function may be used to create a ** zero-filled blob to read or write using the incremental-blob interface. ** ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob |
︙ | ︙ | |||
5549 5550 5551 5552 5553 5554 5555 | ** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or ** [sqlite3_blob_reopen()] on an aborted blob handle immediately return ** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle ** always returns zero. ** ** ^This function sets the database handle error code and message. */ | | | > > > > | > | < < < | < < | < < < < | | > > | | | 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 | ** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or ** [sqlite3_blob_reopen()] on an aborted blob handle immediately return ** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle ** always returns zero. ** ** ^This function sets the database handle error code and message. */ SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); /* ** CAPI3REF: Close A BLOB Handle ** ** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed ** unconditionally. Even if this routine returns an error code, the ** handle is still closed.)^ ** ** ^If the blob handle being closed was opened for read-write access, and if ** the database is in auto-commit mode and there are no other open read-write ** blob handles or active write statements, the current transaction is ** committed. ^If an error occurs while committing the transaction, an error ** code is returned and the transaction rolled back. ** ** Calling this function with an argument that is not a NULL pointer or an ** open blob handle results in undefined behaviour. ^Calling this routine ** with a null pointer (such as would be returned by a failed call to ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function ** is passed a valid open blob handle, the values returned by the ** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning. */ SQLITE_API int SQLITE_STDCALL sqlite3_blob_close(sqlite3_blob *); /* ** CAPI3REF: Return The Size Of An Open BLOB ** ** ^Returns the size in bytes of the BLOB accessible via the ** successfully opened [BLOB handle] in its only argument. ^The ** incremental blob I/O routines can only read or overwriting existing ** blob content; they cannot change the size of a blob. ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. */ SQLITE_API int SQLITE_STDCALL sqlite3_blob_bytes(sqlite3_blob *); /* ** CAPI3REF: Read Data From A BLOB Incrementally ** ** ^(This function is used to read data from an open [BLOB handle] into a ** caller-supplied buffer. N bytes of data are copied into buffer Z ** from the open BLOB, starting at offset iOffset.)^ |
︙ | ︙ | |||
5616 5617 5618 5619 5620 5621 5622 | ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. ** ** See also: [sqlite3_blob_write()]. */ | | | | | > > > > > > | | < | | > < < < | | 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 | ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. ** ** See also: [sqlite3_blob_write()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); /* ** CAPI3REF: Write Data Into A BLOB Incrementally ** ** ^(This function is used to write data into an open [BLOB handle] from a ** caller-supplied buffer. N bytes of data are copied from the buffer Z ** into the open BLOB, starting at offset iOffset.)^ ** ** ^(On success, sqlite3_blob_write() returns SQLITE_OK. ** Otherwise, an [error code] or an [extended error code] is returned.)^ ** ^Unless SQLITE_MISUSE is returned, this function sets the ** [database connection] error code and message accessible via ** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions. ** ** ^If the [BLOB handle] passed as the first argument was not opened for ** writing (the flags parameter to [sqlite3_blob_open()] was zero), ** this function returns [SQLITE_READONLY]. ** ** This function may only modify the contents of the BLOB; it is ** not possible to increase the size of a BLOB using this API. ** ^If offset iOffset is less than N bytes from the end of the BLOB, ** [SQLITE_ERROR] is returned and no data is written. The size of the ** BLOB (and hence the maximum value of N+iOffset) can be determined ** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less ** than zero [SQLITE_ERROR] is returned and no data is written. ** ** ^An attempt to write to an expired [BLOB handle] fails with an ** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred ** before the [BLOB handle] expired are not rolled back by the ** expiration of the handle, though of course those changes might ** have been overwritten by the statement that expired the BLOB handle ** or by other independent statements. ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. ** ** See also: [sqlite3_blob_read()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); /* ** CAPI3REF: Virtual File System Objects ** ** A virtual filesystem (VFS) is an [sqlite3_vfs] object ** that SQLite uses to interact ** with the underlying operating system. Most SQLite builds come with a |
︙ | ︙ | |||
5685 5686 5687 5688 5689 5690 5691 | ** VFS is registered with a name that is NULL or an empty string, ** then the behavior is undefined. ** ** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. ** ^(If the default VFS is unregistered, another VFS is chosen as ** the default. The choice for the new VFS is arbitrary.)^ */ | | | | | | | | | | | | | | | | > > > | | | | | < < | | < | | | < < | | > | | < | | | | | | | | 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 | ** VFS is registered with a name that is NULL or an empty string, ** then the behavior is undefined. ** ** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. ** ^(If the default VFS is unregistered, another VFS is chosen as ** the default. The choice for the new VFS is arbitrary.)^ */ SQLITE_API sqlite3_vfs *SQLITE_STDCALL sqlite3_vfs_find(const char *zVfsName); SQLITE_API int SQLITE_STDCALL sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); SQLITE_API int SQLITE_STDCALL sqlite3_vfs_unregister(sqlite3_vfs*); /* ** CAPI3REF: Mutexes ** ** The SQLite core uses these routines for thread ** synchronization. Though they are intended for internal ** use by SQLite, code that links against SQLite is ** permitted to use any of these routines. ** ** The SQLite source code contains multiple implementations ** of these mutex routines. An appropriate implementation ** is selected automatically at compile-time. The following ** implementations are available in the SQLite core: ** ** <ul> ** <li> SQLITE_MUTEX_PTHREADS ** <li> SQLITE_MUTEX_W32 ** <li> SQLITE_MUTEX_NOOP ** </ul> ** ** The SQLITE_MUTEX_NOOP implementation is a set of routines ** that does no real locking and is appropriate for use in ** a single-threaded application. The SQLITE_MUTEX_PTHREADS and ** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix ** and Windows. ** ** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor ** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex ** implementation is included with the library. In this case the ** application must supply a custom mutex implementation using the ** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function ** before calling sqlite3_initialize() or any other public sqlite3_ ** function that calls sqlite3_initialize(). ** ** ^The sqlite3_mutex_alloc() routine allocates a new ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc() ** routine returns NULL if it is unable to allocate the requested ** mutex. The argument to sqlite3_mutex_alloc() must one of these ** integer constants: ** ** <ul> ** <li> SQLITE_MUTEX_FAST ** <li> SQLITE_MUTEX_RECURSIVE ** <li> SQLITE_MUTEX_STATIC_MASTER ** <li> SQLITE_MUTEX_STATIC_MEM ** <li> SQLITE_MUTEX_STATIC_OPEN ** <li> SQLITE_MUTEX_STATIC_PRNG ** <li> SQLITE_MUTEX_STATIC_LRU ** <li> SQLITE_MUTEX_STATIC_PMEM ** <li> SQLITE_MUTEX_STATIC_APP1 ** <li> SQLITE_MUTEX_STATIC_APP2 ** <li> SQLITE_MUTEX_STATIC_APP3 ** </ul> ** ** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) ** cause sqlite3_mutex_alloc() to create ** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. ** The mutex implementation does not need to make a distinction ** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does ** not want to. SQLite will only request a recursive mutex in ** cases where it really needs one. If a faster non-recursive mutex ** implementation is available on the host platform, the mutex subsystem ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other ** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return ** a pointer to a static preexisting mutex. ^Nine static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or ** SQLITE_MUTEX_RECURSIVE. ** ** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST ** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() ** returns a different mutex on every call. ^For the static ** mutex types, the same mutex is returned on every call that has ** the same type number. ** ** ^The sqlite3_mutex_free() routine deallocates a previously ** allocated dynamic mutex. Attempting to deallocate a static ** mutex results in undefined behavior. ** ** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt ** to enter a mutex. ^If another thread is already within the mutex, ** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return ** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] ** upon successful entry. ^(Mutexes created using ** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. ** In such cases, the ** mutex must be exited an equal number of times before another thread ** can enter.)^ If the same thread tries to enter any mutex other ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() ** will always return SQLITE_BUSY. The SQLite core only ever uses ** sqlite3_mutex_try() as an optimization so this is acceptable ** behavior.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or ** sqlite3_mutex_leave() is a NULL pointer, then all three routines ** behave as no-ops. ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_mutex_alloc(int); SQLITE_API void SQLITE_STDCALL sqlite3_mutex_free(sqlite3_mutex*); SQLITE_API void SQLITE_STDCALL sqlite3_mutex_enter(sqlite3_mutex*); SQLITE_API int SQLITE_STDCALL sqlite3_mutex_try(sqlite3_mutex*); SQLITE_API void SQLITE_STDCALL sqlite3_mutex_leave(sqlite3_mutex*); /* ** CAPI3REF: Mutex Methods Object ** ** An instance of this structure defines the low-level routines ** used to allocate and use mutexes. ** ** Usually, the default mutex implementations provided by SQLite are ** sufficient, however the application has the option of substituting a custom ** implementation for specialized deployments or systems for which SQLite ** does not provide a suitable implementation. In this case, the application ** creates and populates an instance of this structure to pass ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. ** Additionally, an instance of this structure can be used as an ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** ** ^The xMutexInit method defined by this structure is invoked as |
︙ | ︙ | |||
5858 5859 5860 5861 5862 5863 5864 | ** above silently ignore any invocations that pass a NULL pointer instead ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** | | | | | 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 | ** above silently ignore any invocations that pass a NULL pointer instead ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. It must be harmless to ** invoke xMutexInit() multiple times within the same process and without ** intervening calls to xMutexEnd(). Second and subsequent calls to ** xMutexInit() must be no-ops. ** ** xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] ** and its associates). Similarly, xMutexAlloc() must not use SQLite memory ** allocation for a static mutex. ^However xMutexAlloc() may use SQLite ** memory allocation for a fast or recursive mutex. ** ** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is ** called, but only if the prior call to xMutexInit returned SQLITE_OK. ** If xMutexInit fails in any way, it is expected to clean up after itself ** prior to returning. |
︙ | ︙ | |||
5890 5891 5892 5893 5894 5895 5896 | int (*xMutexNotheld)(sqlite3_mutex *); }; /* ** CAPI3REF: Mutex Verification Routines ** ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines | | | | | | | | | | | 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 | int (*xMutexNotheld)(sqlite3_mutex *); }; /* ** CAPI3REF: Mutex Verification Routines ** ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines ** are intended for use inside assert() statements. The SQLite core ** never uses these routines except inside an assert() and applications ** are advised to follow the lead of the core. The SQLite core only ** provides implementations for these routines when it is compiled ** with the SQLITE_DEBUG flag. External mutex implementations ** are only required to provide these routines if SQLITE_DEBUG is ** defined and if NDEBUG is not defined. ** ** These routines should return true if the mutex in their argument ** is held or not held, respectively, by the calling thread. ** ** The implementation is not required to provide versions of these ** routines that actually work. If the implementation does not provide working ** versions of these routines, it should at least provide stubs that always ** return true so that one does not get spurious assertion failures. ** ** If the argument to sqlite3_mutex_held() is a NULL pointer then ** the routine should return 1. This seems counter-intuitive since ** clearly the mutex cannot be held if it does not exist. But ** the reason the mutex does not exist is because the build is not ** using mutexes. And we do not want the assert() containing the ** call to sqlite3_mutex_held() to fail, so a non-zero return is ** the appropriate thing to do. The sqlite3_mutex_notheld() ** interface should also return 1 when given a NULL pointer. */ #ifndef NDEBUG SQLITE_API int SQLITE_STDCALL sqlite3_mutex_held(sqlite3_mutex*); SQLITE_API int SQLITE_STDCALL sqlite3_mutex_notheld(sqlite3_mutex*); #endif /* ** CAPI3REF: Mutex Types ** ** The [sqlite3_mutex_alloc()] interface takes a single argument ** which is one of these integer constants. |
︙ | ︙ | |||
5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 | #define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ #define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ #define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ #define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ #define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ #define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ #define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ /* ** CAPI3REF: Retrieve the mutex for a database connection ** ** ^This interface returns a pointer the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument ** when the [threading mode] is Serialized. ** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ | > > > | | 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 | #define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ #define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ #define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ #define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ #define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ #define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ #define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ #define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */ #define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */ #define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */ /* ** CAPI3REF: Retrieve the mutex for a database connection ** ** ^This interface returns a pointer the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument ** when the [threading mode] is Serialized. ** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ SQLITE_API sqlite3_mutex *SQLITE_STDCALL sqlite3_db_mutex(sqlite3*); /* ** CAPI3REF: Low-Level Control Of Database Files ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated ** with a particular database identified by the second argument. ^The |
︙ | ︙ | |||
5984 5985 5986 5987 5988 5989 5990 | ** or [sqlite3_errmsg()]. The underlying xFileControl method might ** also return SQLITE_ERROR. There is no way to distinguish between ** an incorrect zDbName and an SQLITE_ERROR return from the underlying ** xFileControl method. ** ** See also: [SQLITE_FCNTL_LOCKSTATE] */ | | | | 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 | ** or [sqlite3_errmsg()]. The underlying xFileControl method might ** also return SQLITE_ERROR. There is no way to distinguish between ** an incorrect zDbName and an SQLITE_ERROR return from the underlying ** xFileControl method. ** ** See also: [SQLITE_FCNTL_LOCKSTATE] */ SQLITE_API int SQLITE_STDCALL sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); /* ** CAPI3REF: Testing Interface ** ** ^The sqlite3_test_control() interface is used to read out internal ** state of SQLite and to inject faults into SQLite for testing ** purposes. ^The first parameter is an operation code that determines ** the number, meaning, and operation of all subsequent parameters. ** ** This interface is not for use by applications. It exists solely ** for verifying the correct operation of the SQLite library. Depending ** on how the SQLite library is compiled, this interface might not exist. ** ** The details of the operation codes, their meanings, the parameters ** they take, and what they do are all subject to change without notice. ** Unlike most of the SQLite API, this function is not guaranteed to ** operate consistently from one release to the next. */ SQLITE_API int SQLITE_CDECL sqlite3_test_control(int op, ...); /* ** CAPI3REF: Testing Interface Operation Codes ** ** These constants are the valid operation code parameters used ** as the first argument to [sqlite3_test_control()]. ** |
︙ | ︙ | |||
6031 6032 6033 6034 6035 6036 6037 | #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 | | > > > > > > | | | | < | | < < < > | > > > > > > | 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 | #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 #define SQLITE_TESTCTRL_IMPOSTER 25 #define SQLITE_TESTCTRL_LAST 25 /* ** CAPI3REF: SQLite Runtime Status ** ** ^These interfaces are used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes ** are of the form [status parameters | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after ** *pHighwater is written. ^(Some parameters do not record the highest ** value. For those parameters ** nothing is written into *pHighwater and the resetFlag is ignored.)^ ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** ** ^The sqlite3_status() and sqlite3_status64() routines return ** SQLITE_OK on success and a non-zero [error code] on failure. ** ** If either the current value or the highwater mark is too large to ** be represented by a 32-bit integer, then the values returned by ** sqlite3_status() are undefined. ** ** See also: [sqlite3_db_status()] */ SQLITE_API int SQLITE_STDCALL sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); SQLITE_API int SQLITE_STDCALL sqlite3_status64( int op, sqlite3_int64 *pCurrent, sqlite3_int64 *pHighwater, int resetFlag ); /* ** CAPI3REF: Status Parameters ** KEYWORDS: {status parameters} ** ** These integer constants designate various run-time status parameters |
︙ | ︙ | |||
6181 6182 6183 6184 6185 6186 6187 | ** reset back down to the current value. ** ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ | | | 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 | ** reset back down to the current value. ** ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections ** KEYWORDS: {SQLITE_DBSTATUS options} ** ** These constants are the available integer "verbs" that can be passed as ** the second argument to the [sqlite3_db_status()] interface. |
︙ | ︙ | |||
6223 6224 6225 6226 6227 6228 6229 | ** <dd>This parameter returns the number malloc attempts that might have ** been satisfied using lookaside memory but failed due to all lookaside ** memory already being in use. ** Only the high-water value is meaningful; ** the current value is always zero.)^ ** ** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt> | | | | | 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 | ** <dd>This parameter returns the number malloc attempts that might have ** been satisfied using lookaside memory but failed due to all lookaside ** memory already being in use. ** Only the high-water value is meaningful; ** the current value is always zero.)^ ** ** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt> ** <dd>This parameter returns the approximate number of bytes of heap ** memory used by all pager caches associated with the database connection.)^ ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. ** ** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt> ** <dd>This parameter returns the approximate number of bytes of heap ** memory used to store the schema for all databases associated ** with the connection - main, temp, and any [ATTACH]-ed databases.)^ ** ^The full amount of memory used by the schemas is reported, even if the ** schema memory is shared with other database connections due to ** [shared cache mode] being enabled. ** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. ** ** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt> ** <dd>This parameter returns the approximate number of bytes of heap ** and lookaside memory used by all prepared statements associated with ** the database connection.)^ ** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0. ** </dd> ** ** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt> ** <dd>This parameter returns the number of pager cache hits that have |
︙ | ︙ | |||
6310 6311 6312 6313 6314 6315 6316 | ** to be interrogated.)^ ** ^The current value of the requested counter is returned. ** ^If the resetFlg is true, then the counter is reset to zero after this ** interface call returns. ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ | | | 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 | ** to be interrogated.)^ ** ^The current value of the requested counter is returned. ** ^If the resetFlg is true, then the counter is reset to zero after this ** interface call returns. ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements ** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters} ** ** These preprocessor macros define integer codes that name counter ** values associated with the [sqlite3_stmt_status()] interface. |
︙ | ︙ | |||
6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 | ** an [ATTACH] statement for an attached database. ** ^The S and M arguments passed to ** sqlite3_backup_init(D,N,S,M) identify the [database connection] ** and database name of the source database, respectively. ** ^The source and destination [database connections] (parameters S and D) ** must be different or else sqlite3_backup_init(D,N,S,M) will fail with ** an error. ** ** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is ** returned and an error code and error message are stored in the ** destination [database connection] D. ** ^The error code and message for the failed call to sqlite3_backup_init() ** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or ** [sqlite3_errmsg16()] functions. | > > > > | 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 | ** an [ATTACH] statement for an attached database. ** ^The S and M arguments passed to ** sqlite3_backup_init(D,N,S,M) identify the [database connection] ** and database name of the source database, respectively. ** ^The source and destination [database connections] (parameters S and D) ** must be different or else sqlite3_backup_init(D,N,S,M) will fail with ** an error. ** ** ^A call to sqlite3_backup_init() will fail, returning SQLITE_ERROR, if ** there is already a read or read-write transaction open on the ** destination database. ** ** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is ** returned and an error code and error message are stored in the ** destination [database connection] D. ** ^The error code and message for the failed call to sqlite3_backup_init() ** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or ** [sqlite3_errmsg16()] functions. |
︙ | ︙ | |||
6729 6730 6731 6732 6733 6734 6735 | ** sqlite3_backup_step() call on the same [sqlite3_backup] object, then ** sqlite3_backup_finish() returns the corresponding [error code]. ** ** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** | | > | | | | < < | | < | > > | | 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 | ** sqlite3_backup_step() call on the same [sqlite3_backup] object, then ** sqlite3_backup_finish() returns the corresponding [error code]. ** ** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** [[sqlite3_backup_remaining()]] [[sqlite3_backup_pagecount()]] ** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b> ** ** ^The sqlite3_backup_remaining() routine returns the number of pages still ** to be backed up at the conclusion of the most recent sqlite3_backup_step(). ** ^The sqlite3_backup_pagecount() routine returns the total number of pages ** in the source database at the conclusion of the most recent ** sqlite3_backup_step(). ** ^(The values returned by these functions are only updated by ** sqlite3_backup_step(). If the source database is modified in a way that ** changes the size of the source database or the number of pages remaining, ** those changes are not reflected in the output of sqlite3_backup_pagecount() ** and sqlite3_backup_remaining() until after the next ** sqlite3_backup_step().)^ ** ** <b>Concurrent Usage of Database Handles</b> ** ** ^The source [database connection] may be used by the application for other ** purposes while a backup operation is underway or being initialized. ** ^If SQLite is compiled and configured to support threadsafe database ** connections, then the source database connection may be used concurrently |
︙ | ︙ | |||
6775 6776 6777 6778 6779 6780 6781 | ** The [sqlite3_backup] object itself is partially threadsafe. Multiple ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** APIs are not strictly speaking threadsafe. If they are invoked at the ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ | | | | | | | 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 | ** The [sqlite3_backup] object itself is partially threadsafe. Multiple ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** APIs are not strictly speaking threadsafe. If they are invoked at the ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ SQLITE_API sqlite3_backup *SQLITE_STDCALL sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); SQLITE_API int SQLITE_STDCALL sqlite3_backup_step(sqlite3_backup *p, int nPage); SQLITE_API int SQLITE_STDCALL sqlite3_backup_finish(sqlite3_backup *p); SQLITE_API int SQLITE_STDCALL sqlite3_backup_remaining(sqlite3_backup *p); SQLITE_API int SQLITE_STDCALL sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification ** ** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or ** individual tables within the shared-cache cannot be obtained. See |
︙ | ︙ | |||
6900 6901 6902 6903 6904 6905 6906 | ** ** One way around this problem is to check the extended error code returned ** by an sqlite3_step() call. ^(If there is a blocking connection, then the ** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in ** the special "DROP TABLE/INDEX" case, the extended error code is just ** SQLITE_LOCKED.)^ */ | | | | | | 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 | ** ** One way around this problem is to check the extended error code returned ** by an sqlite3_step() call. ^(If there is a blocking connection, then the ** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in ** the special "DROP TABLE/INDEX" case, the extended error code is just ** SQLITE_LOCKED.)^ */ SQLITE_API int SQLITE_STDCALL sqlite3_unlock_notify( sqlite3 *pBlocked, /* Waiting connection */ void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ ); /* ** CAPI3REF: String Comparison ** ** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications ** and extensions to compare the contents of two buffers containing UTF-8 ** strings in a case-independent fashion, using the same definition of "case ** independence" that SQLite uses internally when comparing identifiers. */ SQLITE_API int SQLITE_STDCALL sqlite3_stricmp(const char *, const char *); SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: String Globbing * ** ^The [sqlite3_strglob(P,X)] interface returns zero if string X matches ** the glob pattern P, and it returns non-zero if string X does not match ** the glob pattern P. ^The definition of glob pattern matching used in ** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the ** SQL dialect used by SQLite. ^The sqlite3_strglob(P,X) function is case ** sensitive. ** ** Note that this routine returns zero on a match and non-zero if the strings ** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlob, const char *zStr); /* ** CAPI3REF: Error Logging Interface ** ** ^The [sqlite3_log()] interface writes a message into the [error log] ** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. ** ^If logging is enabled, the zFormat string and subsequent arguments are |
︙ | ︙ | |||
6954 6955 6956 6957 6958 6959 6960 | ** ** To avoid deadlocks and other threading problems, the sqlite3_log() routine ** will not use dynamically allocated memory. The log message is stored in ** a fixed-length buffer on the stack. If the log message is longer than ** a few hundred characters, it will be truncated to the length of the ** buffer. */ | | | < < | | | 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 | ** ** To avoid deadlocks and other threading problems, the sqlite3_log() routine ** will not use dynamically allocated memory. The log message is stored in ** a fixed-length buffer on the stack. If the log message is longer than ** a few hundred characters, it will be truncated to the length of the ** buffer. */ SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...); /* ** CAPI3REF: Write-Ahead Log Commit Hook ** ** ^The [sqlite3_wal_hook()] function is used to register a callback that ** is invoked each time data is committed to a database in wal mode. ** ** ^(The callback is invoked by SQLite after the commit has taken place and ** the associated write-lock on the database released)^, so the implementation ** may read, write or [checkpoint] the database as required. ** ** ^The first parameter passed to the callback function when it is invoked ** is a copy of the third parameter passed to sqlite3_wal_hook() when ** registering the callback. ^The second is a copy of the database handle. ** ^The third parameter is the name of the database that was written to - ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter |
︙ | ︙ | |||
6991 6992 6993 6994 6995 6996 6997 | ** A single database handle may have at most a single write-ahead log callback ** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any ** previously registered write-ahead log callback. ^Note that the ** [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will ** those overwrite any prior [sqlite3_wal_hook()] settings. */ | | | 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 | ** A single database handle may have at most a single write-ahead log callback ** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any ** previously registered write-ahead log callback. ^Note that the ** [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will ** those overwrite any prior [sqlite3_wal_hook()] settings. */ SQLITE_API void *SQLITE_STDCALL sqlite3_wal_hook( sqlite3*, int(*)(void *,sqlite3*,const char*,int), void* ); /* ** CAPI3REF: Configure an auto-checkpoint |
︙ | ︙ | |||
7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 | ** ^The callback registered by this function replaces any existing callback ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism ** configured by this function. ** ** ^The [wal_autocheckpoint pragma] can be used to invoke this interface ** from SQL. ** ** ^Every new [database connection] defaults to having the auto-checkpoint ** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] ** pages. The use of this interface ** is only necessary if the default setting is found to be suboptimal ** for a particular application. */ | > > > | | < | < < | > > | < < > > > > > > | | > > > > > > | < < < < < | | | | | > > | > | | | > > > > > > > > > | | | < < < | | > > | | | > | | < | | | | | | | | | | | > | | | | | > > > > > > > > | | > | | | | | | | > | | 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 | ** ^The callback registered by this function replaces any existing callback ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism ** configured by this function. ** ** ^The [wal_autocheckpoint pragma] can be used to invoke this interface ** from SQL. ** ** ^Checkpoints initiated by this mechanism are ** [sqlite3_wal_checkpoint_v2|PASSIVE]. ** ** ^Every new [database connection] defaults to having the auto-checkpoint ** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] ** pages. The use of this interface ** is only necessary if the default setting is found to be suboptimal ** for a particular application. */ SQLITE_API int SQLITE_STDCALL sqlite3_wal_autocheckpoint(sqlite3 *db, int N); /* ** CAPI3REF: Checkpoint a database ** ** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to ** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^ ** ** In brief, sqlite3_wal_checkpoint(D,X) causes the content in the ** [write-ahead log] for database X on [database connection] D to be ** transferred into the database file and for the write-ahead log to ** be reset. See the [checkpointing] documentation for addition ** information. ** ** This interface used to be the only way to cause a checkpoint to ** occur. But then the newer and more powerful [sqlite3_wal_checkpoint_v2()] ** interface was added. This interface is retained for backwards ** compatibility and as a convenience for applications that need to manually ** start a callback but which do not need the full power (and corresponding ** complication) of [sqlite3_wal_checkpoint_v2()]. */ SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Checkpoint a database ** ** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint ** operation on database X of [database connection] D in mode M. Status ** information is written back into integers pointed to by L and C.)^ ** ^(The M parameter must be a valid [checkpoint mode]:)^ ** ** <dl> ** <dt>SQLITE_CHECKPOINT_PASSIVE<dd> ** ^Checkpoint as many frames as possible without waiting for any database ** readers or writers to finish, then sync the database file if all frames ** in the log were checkpointed. ^The [busy-handler callback] ** is never invoked in the SQLITE_CHECKPOINT_PASSIVE mode. ** ^On the other hand, passive mode might leave the checkpoint unfinished ** if there are concurrent readers or writers. ** ** <dt>SQLITE_CHECKPOINT_FULL<dd> ** ^This mode blocks (it invokes the ** [sqlite3_busy_handler|busy-handler callback]) until there is no ** database writer and all readers are reading from the most recent database ** snapshot. ^It then checkpoints all frames in the log file and syncs the ** database file. ^This mode blocks new database writers while it is pending, ** but new database readers are allowed to continue unimpeded. ** ** <dt>SQLITE_CHECKPOINT_RESTART<dd> ** ^This mode works the same way as SQLITE_CHECKPOINT_FULL with the addition ** that after checkpointing the log file it blocks (calls the ** [busy-handler callback]) ** until all readers are reading from the database file only. ^This ensures ** that the next writer will restart the log file from the beginning. ** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new ** database writer attempts while it is pending, but does not impede readers. ** ** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd> ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the ** addition that it also truncates the log file to zero bytes just prior ** to a successful return. ** </dl> ** ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in ** the log file or to -1 if the checkpoint could not run because ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not ** NULL,then *pnCkpt is set to the total number of checkpointed frames in the ** log file (including any that were already checkpointed before the function ** was called) or to -1 if the checkpoint could not run due to an error or ** because the database is not in WAL mode. ^Note that upon successful ** completion of an SQLITE_CHECKPOINT_TRUNCATE, the log file will have been ** truncated to zero bytes and so both *pnLog and *pnCkpt will be set to zero. ** ** ^All calls obtain an exclusive "checkpoint" lock on the database file. ^If ** any other process is running a checkpoint operation at the same time, the ** lock cannot be obtained and SQLITE_BUSY is returned. ^Even if there is a ** busy-handler configured, it will not be invoked in this case. ** ** ^The SQLITE_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the ** exclusive "writer" lock on the database file. ^If the writer lock cannot be ** obtained immediately, and a busy-handler is configured, it is invoked and ** the writer lock retried until either the busy-handler returns 0 or the lock ** is successfully obtained. ^The busy-handler is also invoked while waiting for ** database readers as described above. ^If the busy-handler returns 0 before ** the writer lock is obtained or while waiting for database readers, the ** checkpoint operation proceeds from that point in the same way as ** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible ** without blocking any further. ^SQLITE_BUSY is returned in this case. ** ** ^If parameter zDb is NULL or points to a zero length string, then the ** specified operation is attempted on all WAL databases [attached] to ** [database connection] db. In this case the ** values written to output parameters *pnLog and *pnCkpt are undefined. ^If ** an SQLITE_BUSY error is encountered when processing one or more of the ** attached WAL databases, the operation is still attempted on any remaining ** attached databases and SQLITE_BUSY is returned at the end. ^If any other ** error occurs while processing an attached database, processing is abandoned ** and the error code is returned to the caller immediately. ^If no error ** (SQLITE_BUSY or otherwise) is encountered while processing the attached ** databases, SQLITE_OK is returned. ** ** ^If database zDb is the name of an attached database that is not in WAL ** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. ^If ** zDb is not NULL (or a zero length string) and is not the name of any ** attached database, SQLITE_ERROR is returned to the caller. ** ** ^Unless it returns SQLITE_MISUSE, ** the sqlite3_wal_checkpoint_v2() interface ** sets the error information that is queried by ** [sqlite3_errcode()] and [sqlite3_errmsg()]. ** ** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface ** from SQL. */ SQLITE_API int SQLITE_STDCALL sqlite3_wal_checkpoint_v2( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of attached database (or NULL) */ int eMode, /* SQLITE_CHECKPOINT_* value */ int *pnLog, /* OUT: Size of WAL log in frames */ int *pnCkpt /* OUT: Total number of frames checkpointed */ ); /* ** CAPI3REF: Checkpoint Mode Values ** KEYWORDS: {checkpoint mode} ** ** These constants define all valid values for the "checkpoint mode" passed ** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface. ** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the ** meaning of each of these checkpoint modes. */ #define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */ #define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */ #define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */ #define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */ /* ** CAPI3REF: Virtual Table Interface Configuration ** ** This function may be called by either the [xConnect] or [xCreate] method ** of a [virtual table] implementation to configure ** various facets of the virtual table interface. ** ** If this interface is invoked outside the context of an xConnect or ** xCreate virtual table method then the behavior is undefined. ** ** At present, there is only one option that may be configured using ** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options ** may be added in the future. */ SQLITE_API int SQLITE_CDECL sqlite3_vtab_config(sqlite3*, int op, ...); /* ** CAPI3REF: Virtual Table Configuration Options ** ** These macros define the various options to the ** [sqlite3_vtab_config()] interface that [virtual table] implementations ** can use to customize and optimize their behavior. |
︙ | ︙ | |||
7199 7200 7201 7202 7203 7204 7205 | ** This function may only be called from within a call to the [xUpdate] method ** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The ** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], ** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode ** of the SQL statement that triggered the call to the [xUpdate] method of the ** [virtual table]. */ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 | ** This function may only be called from within a call to the [xUpdate] method ** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The ** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], ** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode ** of the SQL statement that triggered the call to the [xUpdate] method of the ** [virtual table]. */ SQLITE_API int SQLITE_STDCALL sqlite3_vtab_on_conflict(sqlite3 *); /* ** CAPI3REF: Conflict resolution modes ** KEYWORDS: {conflict resolution mode} ** ** These constants are returned by [sqlite3_vtab_on_conflict()] to ** inform a [virtual table] implementation what the [ON CONFLICT] mode ** is for the SQL statement being evaluated. ** ** Note that the [SQLITE_IGNORE] constant is also used as a potential ** return value from the [sqlite3_set_authorizer()] callback and that ** [SQLITE_ABORT] is also a [result code]. */ #define SQLITE_ROLLBACK 1 /* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ #define SQLITE_FAIL 3 /* #define SQLITE_ABORT 4 // Also an error code */ #define SQLITE_REPLACE 5 /* ** CAPI3REF: Prepared Statement Scan Status Opcodes ** KEYWORDS: {scanstatus options} ** ** The following constants can be used for the T parameter to the ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a ** different metric for sqlite3_stmt_scanstatus() to return. ** ** When the value returned to V is a string, space to hold that string is ** managed by the prepared statement S and will be automatically freed when ** S is finalized. ** ** <dl> ** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt> ** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be ** set to the total number of times that the X-th loop has run.</dd> ** ** [[SQLITE_SCANSTAT_NVISIT]] <dt>SQLITE_SCANSTAT_NVISIT</dt> ** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be set ** to the total number of rows examined by all iterations of the X-th loop.</dd> ** ** [[SQLITE_SCANSTAT_EST]] <dt>SQLITE_SCANSTAT_EST</dt> ** <dd>^The "double" variable pointed to by the T parameter will be set to the ** query planner's estimate for the average number of rows output from each ** iteration of the X-th loop. If the query planner's estimates was accurate, ** then this value will approximate the quotient NVISIT/NLOOP and the ** product of this value for all prior loops with the same SELECTID will ** be the NLOOP value for the current loop. ** ** [[SQLITE_SCANSTAT_NAME]] <dt>SQLITE_SCANSTAT_NAME</dt> ** <dd>^The "const char *" variable pointed to by the T parameter will be set ** to a zero-terminated UTF-8 string containing the name of the index or table ** used for the X-th loop. ** ** [[SQLITE_SCANSTAT_EXPLAIN]] <dt>SQLITE_SCANSTAT_EXPLAIN</dt> ** <dd>^The "const char *" variable pointed to by the T parameter will be set ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] ** description for the X-th loop. ** ** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECT</dt> ** <dd>^The "int" variable pointed to by the T parameter will be set to the ** "select-id" for the X-th loop. The select-id identifies which query or ** subquery the loop is part of. The main query has a select-id of zero. ** The select-id is the same value as is output in the first column ** of an [EXPLAIN QUERY PLAN] query. ** </dl> */ #define SQLITE_SCANSTAT_NLOOP 0 #define SQLITE_SCANSTAT_NVISIT 1 #define SQLITE_SCANSTAT_EST 2 #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 /* ** CAPI3REF: Prepared Statement Scan Status ** ** This interface returns information about the predicted and measured ** performance for pStmt. Advanced applications can use this ** interface to compare the predicted and the measured performance and ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. ** ** Since this interface is expected to be rarely used, it is only ** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS] ** compile-time option. ** ** The "iScanStatusOp" parameter determines which status information to return. ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior ** of this interface is undefined. ** ^The requested measurement is written into a variable pointed to by ** the "pOut" parameter. ** Parameter "idx" identifies the specific loop to retrieve statistics for. ** Loops are numbered starting from zero. ^If idx is out of range - less than ** zero or greater than or equal to the total number of loops used to implement ** the statement - a non-zero value is returned and the variable that pOut ** points to is unchanged. ** ** ^Statistics might not be available for all loops in all statements. ^In cases ** where there exist loops with no available statistics, this function behaves ** as if the loop did not exist - it returns non-zero and leave the variable ** that pOut points to unchanged. ** ** See also: [sqlite3_stmt_scanstatus_reset()] */ SQLITE_API SQLITE_EXPERIMENTAL int SQLITE_STDCALL sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ int idx, /* Index of loop to report on */ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ void *pOut /* Result written here */ ); /* ** CAPI3REF: Zero Scan-Status Counters ** ** ^Zero all [sqlite3_stmt_scanstatus()] related event counters. ** ** This API is only available if the library is built with pre-processor ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. */ SQLITE_API SQLITE_EXPERIMENTAL void SQLITE_STDCALL sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT |
︙ | ︙ | |||
7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 | #ifdef __cplusplus extern "C" { #endif typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; /* ** Register a geometry callback named zGeom that can be used as part of an ** R-Tree geometry query as follows: ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...) */ | > > > > > > > > > > | < < < | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755 | #ifdef __cplusplus extern "C" { #endif typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info; /* The double-precision datatype used by RTree depends on the ** SQLITE_RTREE_INT_ONLY compile-time option. */ #ifdef SQLITE_RTREE_INT_ONLY typedef sqlite3_int64 sqlite3_rtree_dbl; #else typedef double sqlite3_rtree_dbl; #endif /* ** Register a geometry callback named zGeom that can be used as part of an ** R-Tree geometry query as follows: ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...) */ SQLITE_API int SQLITE_STDCALL sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*), void *pContext ); /* ** A pointer to a structure of the following type is passed as the first ** argument to callbacks registered using rtree_geometry_callback(). */ struct sqlite3_rtree_geometry { void *pContext; /* Copy of pContext passed to s_r_g_c() */ int nParam; /* Size of array aParam[] */ sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */ void *pUser; /* Callback implementation user data */ void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ }; /* ** Register a 2nd-generation geometry callback named zScore that can be ** used as part of an R-Tree geometry query as follows: ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...) */ SQLITE_API int SQLITE_STDCALL sqlite3_rtree_query_callback( sqlite3 *db, const char *zQueryFunc, int (*xQueryFunc)(sqlite3_rtree_query_info*), void *pContext, void (*xDestructor)(void*) ); /* ** A pointer to a structure of the following type is passed as the ** argument to scored geometry callback registered using ** sqlite3_rtree_query_callback(). ** ** Note that the first 5 fields of this structure are identical to ** sqlite3_rtree_geometry. This structure is a subclass of ** sqlite3_rtree_geometry. */ struct sqlite3_rtree_query_info { void *pContext; /* pContext from when function registered */ int nParam; /* Number of function parameters */ sqlite3_rtree_dbl *aParam; /* value of function parameters */ void *pUser; /* callback can use this, if desired */ void (*xDelUser)(void*); /* function to free pUser */ sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */ unsigned int *anQueue; /* Number of pending entries in the queue */ int nCoord; /* Number of coordinates */ int iLevel; /* Level of current node or entry */ int mxLevel; /* The largest iLevel value in the tree */ sqlite3_int64 iRowid; /* Rowid for current entry */ sqlite3_rtree_dbl rParentScore; /* Score of parent node */ int eParentWithin; /* Visibility of parent node */ int eWithin; /* OUT: Visiblity */ sqlite3_rtree_dbl rScore; /* OUT: Write the score here */ }; /* ** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin. */ #define NOT_WITHIN 0 /* Object completely outside of query region */ #define PARTLY_WITHIN 1 /* Object partially overlaps query region */ #define FULLY_WITHIN 2 /* Object fully contained within query region */ #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* ifndef _SQLITE3RTREE_H_ */ |
Changes to src/stash.c.
︙ | ︙ | |||
21 22 23 24 25 26 27 | #include <assert.h> /* ** SQL code to implement the tables needed by the stash. */ static const char zStashInit[] = | | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include <assert.h> /* ** SQL code to implement the tables needed by the stash. */ static const char zStashInit[] = @ CREATE TABLE IF NOT EXISTS "%w".stash( @ stashid INTEGER PRIMARY KEY, -- Unique stash identifier @ vid INTEGER, -- The baseline check-out for this stash @ comment TEXT, -- Comment for this stash. Or NULL @ ctime TIMESTAMP -- When the stash was created @ ); @ CREATE TABLE IF NOT EXISTS "%w".stashfile( @ stashid INTEGER REFERENCES stash, -- Stash that contains this file @ rid INTEGER, -- Baseline content in BLOB table or 0. @ isAdded BOOLEAN, -- True if this is an added file @ isRemoved BOOLEAN, -- True if this file is deleted @ isExec BOOLEAN, -- True if file is executable @ isLink BOOLEAN, -- True if file is a symlink @ origname TEXT, -- Original filename |
︙ | ︙ | |||
59 60 61 62 63 64 65 | Stmt q; /* Query against the vfile table */ Stmt ins; /* Insert statement */ zFile = mprintf("%/", zFName); file_tree_name(zFile, &fname, 1); zTreename = blob_str(&fname); blob_zero(&sql); | | | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | Stmt q; /* Query against the vfile table */ Stmt ins; /* Insert statement */ zFile = mprintf("%/", zFName); file_tree_name(zFile, &fname, 1); zTreename = blob_str(&fname); blob_zero(&sql); blob_append_sql(&sql, "SELECT deleted, isexe, islink, mrid, pathname, coalesce(origname,pathname)" " FROM vfile" " WHERE vid=%d AND (chnged OR deleted OR origname NOT NULL OR mrid==0)", vid ); if( fossil_strcmp(zTreename,".")!=0 ){ blob_append_sql(&sql, " AND (pathname GLOB '%q/*' OR origname GLOB '%q/*'" " OR pathname=%Q OR origname=%Q)", zTreename, zTreename, zTreename, zTreename ); } db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); db_prepare(&ins, "INSERT INTO stashfile(stashid, rid, isAdded, isRemoved, isExec, isLink," "origname, newname, delta)" "VALUES(%d,:rid,:isadd,:isrm,:isexe,:islink,:orig,:new,:content)", stashid ); |
︙ | ︙ | |||
166 167 168 169 170 171 172 | blob_init(&prompt, (const char *) bom, bomSize); #else blob_zero(&prompt); #endif blob_append(&prompt, "\n" "# Enter a description of what is being stashed. Lines beginning\n" | | | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | blob_init(&prompt, (const char *) bom, bomSize); #else blob_zero(&prompt); #endif blob_append(&prompt, "\n" "# Enter a description of what is being stashed. Lines beginning\n" "# with \"#\" are ignored. Stash comments are plain text except\n" "# newlines are not preserved.\n", -1); prompt_for_user_comment(&comment, &prompt); blob_reset(&prompt); zComment = blob_str(&comment); } stashid = db_lget_int("stash-next", 1); |
︙ | ︙ | |||
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | return stashid; } /* ** Apply a stash to the current check-out. */ static void stash_apply(int stashid, int nConflict){ Stmt q; db_prepare(&q, "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta" " FROM stashfile WHERE stashid=%d", stashid ); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); int isRemoved = db_column_int(&q, 1); int isExec = db_column_int(&q, 2); int isLink = db_column_int(&q, 3); const char *zOrig = db_column_text(&q, 4); const char *zNew = db_column_text(&q, 5); char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig); char *zNPath = mprintf("%s%s", g.zLocalRoot, zNew); Blob delta; undo_save(zNew); blob_zero(&delta); if( rid==0 ){ db_ephemeral_blob(&q, 6, &delta); blob_write_to_file(&delta, zNPath); file_wd_setexe(zNPath, isExec); | > > > > > < | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | return stashid; } /* ** Apply a stash to the current check-out. */ static void stash_apply(int stashid, int nConflict){ int vid; Stmt q; db_prepare(&q, "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta" " FROM stashfile WHERE stashid=%d", stashid ); vid = db_lget_int("checkout",0); db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)", filename_collation()); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); int isRemoved = db_column_int(&q, 1); int isExec = db_column_int(&q, 2); int isLink = db_column_int(&q, 3); const char *zOrig = db_column_text(&q, 4); const char *zNew = db_column_text(&q, 5); char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig); char *zNPath = mprintf("%s%s", g.zLocalRoot, zNew); Blob delta; undo_save(zNew); blob_zero(&delta); if( rid==0 ){ db_multi_exec("INSERT OR IGNORE INTO sfile(x) VALUES(%Q)", zNew); db_ephemeral_blob(&q, 6, &delta); blob_write_to_file(&delta, zNPath); file_wd_setexe(zNPath, isExec); }else if( isRemoved ){ fossil_print("DELETE %s\n", zOrig); file_delete(zOPath); }else{ Blob a, b, out, disk; int isNewLink = file_wd_islink(zOPath); db_ephemeral_blob(&q, 6, &delta); |
︙ | ︙ | |||
274 275 276 277 278 279 280 281 282 283 284 285 286 287 | } blob_reset(&delta); if( fossil_strcmp(zOrig,zNew)!=0 ){ undo_save(zOrig); file_delete(zOPath); } } db_finalize(&q); if( nConflict ){ fossil_print( "WARNING: %d merge conflicts - see messages above for details.\n", nConflict); } } | > | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | } blob_reset(&delta); if( fossil_strcmp(zOrig,zNew)!=0 ){ undo_save(zOrig); file_delete(zOPath); } } stash_add_files_in_sfile(vid); db_finalize(&q); if( nConflict ){ fossil_print( "WARNING: %d merge conflicts - see messages above for details.\n", nConflict); } } |
︙ | ︙ | |||
309 310 311 312 313 314 315 | int rid = db_column_int(&q, 0); int isRemoved = db_column_int(&q, 1); int isLink = db_column_int(&q, 3); int isBin1, isBin2; const char *zOrig = db_column_text(&q, 4); const char *zNew = db_column_text(&q, 5); char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig); | | | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | int rid = db_column_int(&q, 0); int isRemoved = db_column_int(&q, 1); int isLink = db_column_int(&q, 3); int isBin1, isBin2; const char *zOrig = db_column_text(&q, 4); const char *zNew = db_column_text(&q, 5); char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig); Blob a, b; if( rid==0 ){ db_ephemeral_blob(&q, 6, &a); fossil_print("ADDED %s\n", zNew); diff_print_index(zNew, diffFlags); isBin1 = 0; isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a); diff_file_mem(&empty, &a, isBin1, isBin2, zNew, zDiffCmd, |
︙ | ︙ | |||
335 336 337 338 339 340 341 342 343 344 345 346 347 348 | } diff_print_index(zNew, diffFlags); isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a); isBin2 = 0; diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); }else{ int isOrigLink = file_wd_islink(zOPath); db_ephemeral_blob(&q, 6, &delta); if( fBaseline==0 ){ if( isOrigLink ){ blob_read_link(&disk, zOPath); }else{ blob_read_from_file(&disk, zOPath); | > | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | } diff_print_index(zNew, diffFlags); isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a); isBin2 = 0; diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); }else{ Blob delta, disk; int isOrigLink = file_wd_islink(zOPath); db_ephemeral_blob(&q, 6, &delta); if( fBaseline==0 ){ if( isOrigLink ){ blob_read_link(&disk, zOPath); }else{ blob_read_from_file(&disk, zOPath); |
︙ | ︙ | |||
361 362 363 364 365 366 367 | isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b); diff_file_mem(fBaseline? &a : &disk, &b, isBin1, isBin2, zNew, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); blob_reset(&a); blob_reset(&b); } if( !fBaseline ) blob_reset(&disk); | < | > | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b); diff_file_mem(fBaseline? &a : &disk, &b, isBin1, isBin2, zNew, zDiffCmd, zBinGlob, fIncludeBinary, diffFlags); blob_reset(&a); blob_reset(&b); } if( !fBaseline ) blob_reset(&disk); blob_reset(&delta); } } db_finalize(&q); } /* ** Drop the indicated stash */ |
︙ | ︙ | |||
455 456 457 458 459 460 461 | ** Show diffs of the current working directory and what that ** directory would be if STASHID were applied. ** ** SUMMARY: ** fossil stash ** fossil stash save ?-m|--comment COMMENT? ?FILES...? ** fossil stash snapshot ?-m|--comment COMMENT? ?FILES...? | | < | | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | ** Show diffs of the current working directory and what that ** directory would be if STASHID were applied. ** ** SUMMARY: ** fossil stash ** fossil stash save ?-m|--comment COMMENT? ?FILES...? ** fossil stash snapshot ?-m|--comment COMMENT? ?FILES...? ** fossil stash list|ls ?-v|--verbose? ?-W|--width <num>? ** fossil stash show ?STASHID? ?DIFF-OPTIONS? ** fossil stash pop ** fossil stash apply ?STASHID? ** fossil stash goto ?STASHID? ** fossil stash rm|drop ?STASHID? ?-a|--all? ** fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS? */ void stash_cmd(void){ const char *zDb; const char *zCmd; int nCmd; int stashid = 0; undo_capture_command_line(); db_must_be_within_tree(); db_open_config(0); db_begin_transaction(); zDb = db_name("localdb"); db_multi_exec(zStashInit /*works-like:"%w,%w"*/, zDb, zDb); if( g.argc<=2 ){ zCmd = "save"; }else{ zCmd = g.argv[2]; } nCmd = strlen(zCmd); if( memcmp(zCmd, "save", nCmd)==0 ){ |
︙ | ︙ | |||
508 509 510 511 512 513 514 | revert_cmd(); }else if( memcmp(zCmd, "snapshot", nCmd)==0 ){ stash_create(); }else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){ Stmt q, q2; | | > > > > > > > > > > | | | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 | revert_cmd(); }else if( memcmp(zCmd, "snapshot", nCmd)==0 ){ stash_create(); }else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){ Stmt q, q2; int n = 0, width; int verboseFlag = find_option("verbose","v",0)!=0; const char *zWidth = find_option("width","W",1); if( zWidth ){ width = atoi(zWidth); if( (width!=0) && (width<=46) ){ fossil_fatal("-W|--width value must be >46 or 0"); } }else{ width = -1; } if( !verboseFlag ){ verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ } verify_all_options(); db_prepare(&q, "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid)," " comment, datetime(ctime) FROM stash" " ORDER BY ctime" ); if( verboseFlag ){ db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname" " FROM stashfile WHERE stashid=$id"); } while( db_step(&q)==SQLITE_ROW ){ int stashid = db_column_int(&q, 0); const char *zCom; n++; fossil_print("%5d: [%.14s] on %s\n", stashid, db_column_text(&q, 1), db_column_text(&q, 3) ); zCom = db_column_text(&q, 2); if( zCom && zCom[0] ){ fossil_print(" "); comment_print(zCom, 0, 7, width, g.comFmtFlags); } if( verboseFlag ){ db_bind_int(&q2, "$id", stashid); while( db_step(&q2)==SQLITE_ROW ){ int isAdded = db_column_int(&q2, 0); int isRemoved = db_column_int(&q2, 1); const char *zOrig = db_column_text(&q2, 2); |
︙ | ︙ | |||
566 567 568 569 570 571 572 | if( n==0 ) fossil_print("empty stash\n"); }else if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){ int allFlag = find_option("all", "a", 0)!=0; if( allFlag ){ Blob ans; char cReply; | < | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | if( n==0 ) fossil_print("empty stash\n"); }else if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){ int allFlag = find_option("all", "a", 0)!=0; if( allFlag ){ Blob ans; char cReply; prompt_user("This action is not undoable. Continue (y/N)? ", &ans); cReply = blob_str(&ans)[0]; if( cReply=='y' || cReply=='Y' ){ db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;"); } }else if( g.argc>=4 ){ int i; |
︙ | ︙ | |||
607 608 609 610 611 612 613 | undo_begin(); stash_apply(stashid, 0); undo_finish(); }else if( memcmp(zCmd, "goto", nCmd)==0 ){ int nConflict; int vid; | < | 621 622 623 624 625 626 627 628 629 630 631 632 633 634 | undo_begin(); stash_apply(stashid, 0); undo_finish(); }else if( memcmp(zCmd, "goto", nCmd)==0 ){ int nConflict; int vid; if( g.argc>4 ) usage("apply STASHID"); stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); undo_begin(); vid = db_int(0, "SELECT vid FROM stash WHERE stashid=%d", stashid); nConflict = update_to(vid); stash_apply(stashid, nConflict); db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN " |
︙ | ︙ | |||
634 635 636 637 638 639 640 | if( find_option("tk",0,0)!=0 ){ db_close(0); diff_tk((zCmd[0]=='s' ? "stash show" : "stash diff"), 3); return; } if( find_option("internal","i",0)==0 ){ | | | 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 | if( find_option("tk",0,0)!=0 ){ db_close(0); diff_tk((zCmd[0]=='s' ? "stash show" : "stash diff"), 3); return; } if( find_option("internal","i",0)==0 ){ zDiffCmd = diff_command_external(memcmp(zCmd, "gdiff", nCmd)==0); } diffFlags = diff_options(); if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; if( g.argc>4 ) usage(mprintf("%s STASHID", zCmd)); if( zDiffCmd ){ zBinGlob = diff_get_binary_glob(); fIncludeBinary = diff_include_binary_files(); |
︙ | ︙ |
Changes to src/stat.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement the stat web page ** */ | | > | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement the stat web page ** */ #include "VERSION.h" #include "config.h" #include <string.h> #include "stat.h" /* ** For a sufficiently large integer, provide an alternative ** representation as MB or GB or TB. */ void bigSizeName(int nOut, char *zOut, sqlite3_int64 v){ if( v<100000 ){ sqlite3_snprintf(nOut, zOut, "%lld bytes", v); }else if( v<1000000000 ){ sqlite3_snprintf(nOut, zOut, "%lld bytes (%.1fMB)", v, (double)v/1000000.0); }else{ sqlite3_snprintf(nOut, zOut, "%lld bytes (%.1fGB)", |
︙ | ︙ | |||
46 47 48 49 50 51 52 53 54 | void stat_page(void){ i64 t, fsize; int n, m; int szMax, szAvg; const char *zDb; int brief; char zBuf[100]; login_check_credentials(); | > | > > > > > | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | void stat_page(void){ i64 t, fsize; int n, m; int szMax, szAvg; const char *zDb; int brief; char zBuf[100]; const char *p; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } brief = P("brief")!=0; style_header("Repository Statistics"); style_adunit_config(ADUNIT_RIGHT_OK); if( g.perm.Admin ){ style_submenu_element("URLs", "URLs and Checkouts", "urllist"); style_submenu_element("Schema", "Repository Schema", "repo_schema"); style_submenu_element("Web-Cache", "Web-Cache Stats", "cachestat"); } style_submenu_element("Activity Reports", 0, "reports"); style_submenu_element("SHA1 Collisions", 0, "hash-collisions"); @ <table class="label-value"> @ <tr><th>Repository Size:</th><td> fsize = file_size(g.zRepositoryName); bigSizeName(sizeof(zBuf), zBuf, fsize); @ %s(zBuf) @ </td></tr> if( !brief ){ @ <tr><th>Number Of Artifacts:</th><td> n = db_int(0, "SELECT count(*) FROM blob"); m = db_int(0, "SELECT count(*) FROM delta"); @ %d(n) (%d(n-m) fulltext and %d(m) deltas) @ </td></tr> if( n>0 ){ int a, b; Stmt q; @ <tr><th>Uncompressed Artifact Size:</th><td> db_prepare(&q, "SELECT total(size), avg(size), max(size)" " FROM blob WHERE size>0 /*scan*/"); db_step(&q); t = db_column_int64(&q, 0); szAvg = db_column_int(&q, 1); szMax = db_column_int(&q, 2); db_finalize(&q); bigSizeName(sizeof(zBuf), zBuf, t); @ %d(szAvg) bytes average, %d(szMax) bytes max, %s(zBuf) total |
︙ | ︙ | |||
115 116 117 118 119 120 121 | @ </td></tr> } @ <tr><th>Duration Of Project:</th><td> n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" " + 0.99"); @ %d(n) days or approximately %.2f(n/365.2425) years. @ </td></tr> | > > | > > | > > > > | | | | | | | < < | > > > > > > > > > > > > > > > > > > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | @ </td></tr> } @ <tr><th>Duration Of Project:</th><td> n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" " + 0.99"); @ %d(n) days or approximately %.2f(n/365.2425) years. @ </td></tr> p = db_get("project-code", 0); if( p ){ @ <tr><th>Project ID:</th><td>%h(p)</td></tr> } @ <tr><th>Server ID:</th><td>%h(db_get("server-code",""))</td></tr> @ <tr><th>Fossil Version:</th><td> @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] @ </td></tr> @ <tr><th>SQLite Version:</th><td>%.19s(sqlite3_sourceid()) @ [%.10s(&sqlite3_sourceid()[20])] (%s(sqlite3_libversion()))</td></tr> @ <tr><th>Schema Version:</th><td>%h(g.zAuxSchema)</td></tr> @ <tr><th>Repository Rebuilt:</th><td> @ %h(db_get_mtime("rebuilt","%Y-%m-%d %H:%M:%S","Never")) @ By Fossil %h(db_get("rebuilt","Unknown"))</td></tr> @ <tr><th>Database Stats:</th><td> zDb = db_name("repository"); @ %d(db_int(0, "PRAGMA \"%w\".page_count", zDb)) pages, @ %d(db_int(0, "PRAGMA \"%w\".page_size", zDb)) bytes/page, @ %d(db_int(0, "PRAGMA \"%w\".freelist_count", zDb)) free pages, @ %s(db_text(0, "PRAGMA \"%w\".encoding", zDb)), @ %s(db_text(0, "PRAGMA \"%w\".journal_mode", zDb)) mode @ </td></tr> @ </table> style_footer(); } /* ** COMMAND: dbstat* ** ** Usage: %fossil dbstat OPTIONS ** ** Shows statistics and global information about the repository. ** ** Options: ** ** --brief|-b Only show essential elements ** --db-check Run a PRAGMA quick_check on the repository database ** --omit-version-info Omit the SQLite and Fossil version information */ void dbstat_cmd(void){ i64 t, fsize; int n, m; int szMax, szAvg; const char *zDb; int brief; int omitVers; /* Omit Fossil and SQLite version information */ int dbCheck; /* True for the --db-check option */ char zBuf[100]; const int colWidth = -19 /* printf alignment/width for left column */; const char *p, *z; brief = find_option("brief", "b",0)!=0; omitVers = find_option("omit-version-info", 0, 0)!=0; dbCheck = find_option("db-check",0,0)!=0; db_find_and_open_repository(0,0); /* We should be done with options.. */ verify_all_options(); if( (z = db_get("project-name",0))!=0 || (z = db_get("short-project-name",0))!=0 ){ fossil_print("%*s%s\n", colWidth, "project-name:", z); } fsize = file_size(g.zRepositoryName); bigSizeName(sizeof(zBuf), zBuf, fsize); fossil_print( "%*s%s\n", colWidth, "repository-size:", zBuf ); if( !brief ){ n = db_int(0, "SELECT count(*) FROM blob"); m = db_int(0, "SELECT count(*) FROM delta"); fossil_print("%*s%d (stored as %d full text and %d delta blobs)\n", |
︙ | ︙ | |||
191 192 193 194 195 196 197 | }else{ b = 1; } a = t/fsize; fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b); } n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'"); | | | | > > > > > > > | > > > > > > > | | | | | | | < > > | | | | | | > > | | < | > > | 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | }else{ b = 1; } a = t/fsize; fossil_print("%*s%d:%d\n", colWidth, "compression-ratio:", a, b); } n = db_int(0, "SELECT COUNT(*) FROM event e WHERE e.type='ci'"); fossil_print("%*s%d\n", colWidth, "check-ins:", n); n = db_int(0, "SELECT count(*) FROM filename /*scan*/"); fossil_print("%*s%d across all branches\n", colWidth, "files:", n); n = db_int(0, "SELECT count(*) FROM tag /*scan*/" " WHERE tagname GLOB 'wiki-*'"); m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='w'"); fossil_print("%*s%d (%d changes)\n", colWidth, "wiki-pages:", n, m); n = db_int(0, "SELECT count(*) FROM tag /*scan*/" " WHERE tagname GLOB 'tkt-*'"); m = db_int(0, "SELECT COUNT(*) FROM event WHERE type='t'"); fossil_print("%*s%d (%d changes)\n", colWidth, "tickets:", n, m); n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='e'"); fossil_print("%*s%d\n", colWidth, "events:", n); n = db_int(0, "SELECT COUNT(*) FROM event WHERE type='g'"); fossil_print("%*s%d\n", colWidth, "tag-changes:", n); z = db_text(0, "SELECT datetime(mtime) || ' - about ' ||" " CAST(julianday('now') - mtime AS INTEGER)" " || ' days ago' FROM event " " ORDER BY mtime DESC LIMIT 1"); fossil_print("%*s%s\n", colWidth, "latest-change:", z); } n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)" " + 0.99"); fossil_print("%*s%d days or approximately %.2f years.\n", colWidth, "project-age:", n, n/365.2425); p = db_get("project-code", 0); if( p ){ fossil_print("%*s%s\n", colWidth, "project-id:", p); } #if 0 /* Server-id is not useful information any more */ fossil_print("%*s%s\n", colWidth, "server-id:", db_get("server-code", 0)); #endif fossil_print("%*s%s\n", colWidth, "schema-version:", g.zAuxSchema); if( !omitVers ){ fossil_print("%*s%s %s [%s] (%s)\n", colWidth, "fossil-version:", MANIFEST_DATE, MANIFEST_VERSION, RELEASE_VERSION, COMPILER_NAME); fossil_print("%*s%.19s [%.10s] (%s)\n", colWidth, "sqlite-version:", sqlite3_sourceid(), &sqlite3_sourceid()[20], sqlite3_libversion()); } zDb = db_name("repository"); fossil_print("%*s%d pages, %d bytes/pg, %d free pages, " "%s, %s mode\n", colWidth, "database-stats:", db_int(0, "PRAGMA \"%w\".page_count", zDb), db_int(0, "PRAGMA \"%w\".page_size", zDb), db_int(0, "PRAGMA \"%w\".freelist_count", zDb), db_text(0, "PRAGMA \"%w\".encoding", zDb), db_text(0, "PRAGMA \"%w\".journal_mode", zDb)); if( dbCheck ){ fossil_print("%*s%s\n", colWidth, "database-check:", db_text(0, "PRAGMA quick_check(1)")); } } /* ** WEBPAGE: urllist ** ** Show ways in which this repository has been accessed */ void urllist_page(void){ Stmt q; int cnt; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_header("URLs and Checkouts"); style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Stat", "Repository Stats", "stat"); style_submenu_element("Schema", "Repository Schema", "repo_schema"); @ <div class="section">URLs</div> @ <table border="0" width='100%%'> db_prepare(&q, "SELECT substr(name,9), datetime(mtime,'unixepoch')" " FROM config WHERE name GLOB 'baseurl:*' ORDER BY 2 DESC"); cnt = 0; while( db_step(&q)==SQLITE_ROW ){ @ <tr><td width='100%%'>%h(db_column_text(&q,0))</td> |
︙ | ︙ | |||
279 280 281 282 283 284 285 | db_finalize(&q); if( cnt==0 ){ @ <tr><td>(none)</td> } @ </table> style_footer(); } | > > > > > > > > > > > > > > > > > > > > > > > > > | 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | db_finalize(&q); if( cnt==0 ){ @ <tr><td>(none)</td> } @ </table> style_footer(); } /* ** WEBPAGE: repo_schema ** ** Show the repository schema */ void repo_schema_page(void){ Stmt q; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_header("Repository Schema"); style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Stat", "Repository Stats", "stat"); style_submenu_element("URLs", "URLs and Checkouts", "urllist"); db_prepare(&q, "SELECT sql FROM %s.sqlite_master WHERE sql IS NOT NULL", db_name("repository")); @ <pre> while( db_step(&q)==SQLITE_ROW ){ @ %h(db_column_text(&q, 0)); } @ </pre> db_finalize(&q); style_footer(); } |
Added src/statrep.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 | /* ** Copyright (c) 2013 Stephen Beal ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement the /reports web page. ** */ #include "config.h" #include <string.h> #include <time.h> #include "statrep.h" /* ** Used by stats_report_xxxxx() to remember which type of events ** to show. Populated by stats_report_init_view() and holds the ** return value of that function. */ static int statsReportType = 0; /* ** Set by stats_report_init_view() to one of the y=XXXX values ** accepted by /timeline?y=XXXX. */ static const char *statsReportTimelineYFlag = NULL; /* ** Generate a submenu element with a single parameter change. */ static void statrep_submenu( HQuery *pUrl, /* Base URL */ const char *zMenuName, /* Submenu name */ const char *zParam, /* Parameter value to add or change */ const char *zValue, /* Value of the new parameter */ const char *zRemove /* Parameter to omit */ ){ style_submenu_element(zMenuName, zMenuName, "%s", url_render(pUrl, zParam, zValue, zRemove, 0)); } /* ** Creates a TEMP VIEW named v_reports which is a wrapper around the ** EVENT table filtered on event.type. It looks for the request ** parameter 'type' (reminder: we "should" use 'y' for consistency ** with /timeline, but /reports uses 'y' for the year) and expects it ** to contain one of the conventional values from event.type or the ** value "all", which is treated as equivalent to "*". By default (if ** no 'y' is specified), "*" is assumed (that is also the default for ** invalid/unknown filter values). That 'y' filter is the one used for ** the event list. Note that a filter of "*" or "all" is equivalent to ** querying against the full event table. The view, however, adds an ** abstraction level to simplify the implementation code for the ** various /reports pages. ** ** Returns one of: 'c', 'w', 'g', 't', 'e', representing the type of ** filter it applies, or '*' if no filter is applied (i.e. if "all" is ** used). */ static int stats_report_init_view(){ const char *zType = PD("type","*"); /* analog to /timeline?y=... */ const char *zRealType = NULL; /* normalized form of zType */ int rc = 0; /* result code */ assert( !statsReportType && "Must not be called more than once." ); switch( (zType && *zType) ? *zType : 0 ){ case 'c': case 'C': zRealType = "ci"; rc = *zRealType; break; case 'e': case 'E': zRealType = "e"; rc = *zRealType; break; case 'g': case 'G': zRealType = "g"; rc = *zRealType; break; case 't': case 'T': zRealType = "t"; rc = *zRealType; break; case 'w': case 'W': zRealType = "w"; rc = *zRealType; break; default: rc = '*'; break; } assert(0 != rc); if(zRealType){ statsReportTimelineYFlag = zRealType; db_multi_exec("CREATE TEMP VIEW v_reports AS " "SELECT * FROM event WHERE type GLOB %Q", zRealType); }else{ statsReportTimelineYFlag = "a"; db_multi_exec("CREATE TEMP VIEW v_reports AS " "SELECT * FROM event"); } return statsReportType = rc; } /* ** Returns a string suitable (for a given value of suitable) for ** use in a label with the header of the /reports pages, dependent ** on the 'type' flag. See stats_report_init_view(). ** The returned bytes are static. */ static const char *stats_report_label_for_type(){ assert( statsReportType && "Must call stats_report_init_view() first." ); switch( statsReportType ){ case 'c': return "check-ins"; case 'e': return "technotes"; case 'w': return "wiki changes"; case 't': return "ticket changes"; case 'g': return "tag changes"; default: return "all types"; } } /* ** A helper for the /reports family of pages which prints out a menu ** of links for the various type=XXX flags. zCurrentViewName must be ** the name/value of the 'view' parameter which is in effect at the ** time this is called. e.g. if called from the 'byuser' view then ** zCurrentViewName must be "byuser". Any URL parameters which need to ** be added to the generated URLs should be passed in zParam. The ** caller is expected to have already encoded any zParam in the %T or ** %t encoding. */ static void stats_report_event_types_menu(const char *zCurrentViewName, const char *zParam){ char *zTop; if(zParam && !*zParam){ zParam = NULL; } zTop = mprintf("%s/reports?view=%s%s%s", g.zTop, zCurrentViewName, zParam ? "&" : "", zParam); cgi_printf("<div>"); cgi_printf("<span>Types:</span> "); if('*' == statsReportType){ cgi_printf(" <strong>all</strong>", zTop); }else{ cgi_printf(" <a href='%s'>all</a>", zTop); } if('c' == statsReportType){ cgi_printf(" <strong>check-ins</strong>", zTop); }else{ cgi_printf(" <a href='%s&type=ci'>check-ins</a>", zTop); } if('e' == statsReportType){ cgi_printf(" <strong>technotes</strong>", zTop); }else{ cgi_printf(" <a href='%s&type=e'>technotes</a>", zTop); } if( 't' == statsReportType ){ cgi_printf(" <strong>tickets</strong>", zTop); }else{ cgi_printf(" <a href='%s&type=t'>tickets</a>", zTop); } if( 'g' == statsReportType ){ cgi_printf(" <strong>tags</strong>", zTop); }else{ cgi_printf(" <a href='%s&type=g'>tags</a>", zTop); } if( 'w' == statsReportType ){ cgi_printf(" <strong>wiki</strong>", zTop); }else{ cgi_printf(" <a href='%s&type=w'>wiki</a>", zTop); } fossil_free(zTop); cgi_printf("</div>"); } /* ** Helper for stats_report_by_month_year(), which generates a list of ** week numbers. zTimeframe should be either a timeframe in the form YYYY ** or YYYY-MM. */ static void stats_report_output_week_links(const char *zTimeframe){ Stmt stWeek = empty_Stmt; char yearPart[5] = {0,0,0,0,0}; memcpy(yearPart, zTimeframe, 4); db_prepare(&stWeek, "SELECT DISTINCT strftime('%%W',mtime) AS wk, " "count(*) AS n, " "substr(date(mtime),1,%d) AS ym " "FROM v_reports " "WHERE ym=%Q AND mtime < current_timestamp " "GROUP BY wk ORDER BY wk", strlen(zTimeframe), zTimeframe); while( SQLITE_ROW == db_step(&stWeek) ){ const char *zWeek = db_column_text(&stWeek,0); const int nCount = db_column_int(&stWeek,1); cgi_printf("<a href='%R/timeline?" "yw=%t-%t&n=%d&y=%s'>%s</a>", yearPart, zWeek, nCount, statsReportTimelineYFlag, zWeek); } db_finalize(&stWeek); } /* ** Implements the "byyear" and "bymonth" reports for /reports. ** If includeMonth is true then it generates the "bymonth" report, ** else the "byyear" report. If zUserName is not NULL and not empty ** then the report is restricted to events created by the named user ** account. */ static void stats_report_by_month_year(char includeMonth, char includeWeeks, const char *zUserName){ Stmt query = empty_Stmt; int nRowNumber = 0; /* current TR number */ int nEventTotal = 0; /* Total event count */ int rowClass = 0; /* counter for alternating row colors */ Blob sql = empty_blob; /* SQL */ const char *zTimeLabel = includeMonth ? "Year/Month" : "Year"; char zPrevYear[5] = {0}; /* For keeping track of when we change years while looping */ int nEventsPerYear = 0; /* Total event count for the current year */ char showYearTotal = 0; /* Flag telling us when to show the per-year event totals */ Blob header = empty_blob; /* Page header text */ int nMaxEvents = 1; /* for calculating length of graph bars. */ int iterations = 0; /* number of weeks/months we iterate over */ stats_report_init_view(); stats_report_event_types_menu( includeMonth ? "bymonth" : "byyear", NULL ); blob_appendf(&header, "Timeline Events (%s) by year%s", stats_report_label_for_type(), (includeMonth ? "/month" : "")); blob_append_sql(&sql, "SELECT substr(date(mtime),1,%d) AS timeframe, " "count(*) AS eventCount " "FROM v_reports ", includeMonth ? 7 : 4); if(zUserName&&*zUserName){ blob_append_sql(&sql, " WHERE user=%Q ", zUserName); blob_appendf(&header," for user %q", zUserName); } blob_append(&sql, " GROUP BY timeframe" " ORDER BY timeframe DESC", -1); db_prepare(&query, "%s", blob_sql_text(&sql)); blob_reset(&sql); @ <h1>%b(&header)</h1> @ <table class='statistics-report-table-events' border='0' cellpadding='2' @ cellspacing='0' id='statsTable'> @ <thead> @ <th>%s(zTimeLabel)</th> @ <th>Events</th> @ <th width='90%%'><!-- relative commits graph --></th> @ </thead><tbody> blob_reset(&header); /* Run the query twice. The first time we calculate the maximum number of events for a given row. Maybe someone with better SQL Fu can re-implement this with a single query. */ while( SQLITE_ROW == db_step(&query) ){ const int nCount = db_column_int(&query, 1); if(nCount>nMaxEvents){ nMaxEvents = nCount; } ++iterations; } db_reset(&query); while( SQLITE_ROW == db_step(&query) ){ const char *zTimeframe = db_column_text(&query, 0); const int nCount = db_column_int(&query, 1); int nSize = nCount ? (int)(100 * nCount / nMaxEvents) : 1; showYearTotal = 0; if(!nSize) nSize = 1; if(includeMonth){ /* For Month/year view, add a separator for each distinct year. */ if(!*zPrevYear || (0!=fossil_strncmp(zPrevYear,zTimeframe,4))){ showYearTotal = *zPrevYear; if(showYearTotal){ rowClass = ++nRowNumber % 2; @ <tr class='row%d(rowClass)'> @ <td></td> @ <td colspan='2'>Yearly total: %d(nEventsPerYear)</td> @</tr> } nEventsPerYear = 0; memcpy(zPrevYear,zTimeframe,4); rowClass = ++nRowNumber % 2; @ <tr class='row%d(rowClass)'> @ <th colspan='3' class='statistics-report-row-year'>%s(zPrevYear)</th> @ </tr> } } rowClass = ++nRowNumber % 2; nEventTotal += nCount; nEventsPerYear += nCount; @<tr class='row%d(rowClass)'> @ <td> if(includeMonth){ cgi_printf("<a href='%R/timeline?" "ym=%t&n=%d&y=%s", zTimeframe, nCount, statsReportTimelineYFlag ); /* Reminder: n=nCount is not actually correct for bymonth unless that was the only user who caused events. */ if( zUserName && *zUserName ){ cgi_printf("&u=%t", zUserName); } cgi_printf("' target='_new'>%s</a>",zTimeframe); }else { cgi_printf("<a href='?view=byweek&y=%s&type=%c", zTimeframe, (char)statsReportType); if(zUserName && *zUserName){ cgi_printf("&u=%t", zUserName); } cgi_printf("'>%s</a>", zTimeframe); } @ </td><td>%d(nCount)</td> @ <td> @ <div class='statistics-report-graph-line' @ style='width:%d(nSize)%%;'> </div> @ </td> @</tr> if(includeWeeks){ /* This part works fine for months but it terribly slow (4.5s on my PC), so it's only shown for by-year for now. Suggestions/patches for a better/faster layout are welcomed. */ @ <tr class='row%d(rowClass)'> @ <td colspan='2' class='statistics-report-week-number-label'>Week #:</td> @ <td class='statistics-report-week-of-year-list'> stats_report_output_week_links(zTimeframe); @ </td></tr> } /* Potential improvement: calculate the min/max event counts and use percent-based graph bars. */ } db_finalize(&query); if(includeMonth && !showYearTotal && *zPrevYear){ /* Add final year total separator. */ rowClass = ++nRowNumber % 2; @ <tr class='row%d(rowClass)'> @ <td></td> @ <td colspan='2'>Yearly total: %d(nEventsPerYear)</td> @</tr> } @ </tbody></table> if(nEventTotal){ const char *zAvgLabel = includeMonth ? "month" : "year"; int nAvg = iterations ? (nEventTotal/iterations) : 0; @ <br><div>Total events: %d(nEventTotal) @ <br>Average per active %s(zAvgLabel): %d(nAvg) @ </div> } if( !includeMonth ){ output_table_sorting_javascript("statsTable","tnx",-1); } } /* ** Implements the "byuser" view for /reports. */ static void stats_report_by_user(){ Stmt query = empty_Stmt; int nRowNumber = 0; /* current TR number */ int nEventTotal = 0; /* Total event count */ int rowClass = 0; /* counter for alternating row colors */ int nMaxEvents = 1; /* max number of events for all rows. */ stats_report_init_view(); stats_report_event_types_menu("byuser", NULL); db_prepare(&query, "SELECT user, " "COUNT(*) AS eventCount " "FROM v_reports " "GROUP BY user ORDER BY eventCount DESC"); @ <h1>Timeline Events @ (%s(stats_report_label_for_type())) by User</h1> @ <table class='statistics-report-table-events' border='0' @ cellpadding='2' cellspacing='0' id='statsTable'> @ <thead><tr> @ <th>User</th> @ <th>Events</th> @ <th width='90%%'><!-- relative commits graph --></th> @ </tr></thead><tbody> while( SQLITE_ROW == db_step(&query) ){ const int nCount = db_column_int(&query, 1); if(nCount>nMaxEvents){ nMaxEvents = nCount; } } db_reset(&query); while( SQLITE_ROW == db_step(&query) ){ const char *zUser = db_column_text(&query, 0); const int nCount = db_column_int(&query, 1); int nSize = nCount ? (int)(100 * nCount / nMaxEvents) : 0; if(!nCount) continue /* arguable! Possible? */; else if(!nSize) nSize = 1; rowClass = ++nRowNumber % 2; nEventTotal += nCount; @<tr class='row%d(rowClass)'> @ <td> @ <a href="?view=bymonth&user=%h(zUser)&type=%c((char)statsReportType)">%h(zUser)</a> @ </td><td data-sortkey='%08x(-nCount)'>%d(nCount)</td> @ <td> @ <div class='statistics-report-graph-line' @ style='width:%d(nSize)%%;'> </div> @ </td> @</tr> /* Potential improvement: calculate the min/max event counts and use percent-based graph bars. */ } @ </tbody></table> db_finalize(&query); output_table_sorting_javascript("statsTable","tkx",2); } /* ** Implements the "byfile" view for /reports. */ static void stats_report_by_file(){ Stmt query; int mxEvent = 1; /* max number of events across all rows */ int nRowNumber = 0; db_multi_exec( "CREATE TEMP TABLE statrep(filename, cnt);" "INSERT INTO statrep(filename, cnt)" " SELECT filename.name, count(distinct mlink.mid)" " FROM filename, mlink" " WHERE filename.fnid=mlink.fnid" " GROUP BY 1" ); db_prepare(&query, "SELECT filename, cnt FROM statrep ORDER BY cnt DESC, filename /*sort*/" ); mxEvent = db_int(1, "SELECT max(cnt) FROM statrep"); @ <h1>Check-ins Per File</h1> @ <table class='statistics-report-table-events' border='0' @ cellpadding='2' cellspacing='0' id='statsTable'> @ <thead><tr> @ <th>File</th> @ <th>Check-ins</th> @ <th width='90%%'><!-- relative commits graph --></th> @ </tr></thead><tbody> while( SQLITE_ROW == db_step(&query) ){ const char *zFile = db_column_text(&query, 0); const int n = db_column_int(&query, 1); int sz; if( n<=0 ) continue; sz = (int)(100*n/mxEvent); if( sz==0 ) sz = 1; @<tr class='row%d(++nRowNumber%2)'> @ <td>%z(href("%R/finfo?name=%T",zFile))%h(zFile)</a></td> @ <td>%d(n)</td> @ <td> @ <div class='statistics-report-graph-line' @ style='width:%d(sz)%%;'> </div> @ </td> @</tr> } @ </tbody></table> db_finalize(&query); output_table_sorting_javascript("statsTable","tNx",2); } /* ** Implements the "byweekday" view for /reports. */ static void stats_report_day_of_week(){ Stmt query = empty_Stmt; int nRowNumber = 0; /* current TR number */ int nEventTotal = 0; /* Total event count */ int rowClass = 0; /* counter for alternating row colors */ int nMaxEvents = 1; /* max number of events for all rows. */ static const char *const daysOfWeek[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; stats_report_init_view(); stats_report_event_types_menu("byweekday", NULL); db_prepare(&query, "SELECT cast(mtime %% 7 AS INTEGER) dow, " "COUNT(*) AS eventCount " "FROM v_reports " "GROUP BY dow ORDER BY dow"); @ <h1>Timeline Events @ (%s(stats_report_label_for_type())) by Day of the Week</h1> @ <table class='statistics-report-table-events' border='0' @ cellpadding='2' cellspacing='0' id='statsTable'> @ <thead><tr> @ <th>DoW</th> @ <th>Day</th> @ <th>Events</th> @ <th width='90%%'><!-- relative commits graph --></th> @ </tr></thead><tbody> while( SQLITE_ROW == db_step(&query) ){ const int nCount = db_column_int(&query, 1); if(nCount>nMaxEvents){ nMaxEvents = nCount; } } db_reset(&query); while( SQLITE_ROW == db_step(&query) ){ const int dayNum =db_column_int(&query, 0); const int nCount = db_column_int(&query, 1); int nSize = nCount ? (int)(100 * nCount / nMaxEvents) : 0; if(!nCount) continue /* arguable! Possible? */; else if(!nSize) nSize = 1; rowClass = ++nRowNumber % 2; nEventTotal += nCount; @<tr class='row%d(rowClass)'> @ <td>%d(dayNum)</td> @ <td>%s(daysOfWeek[dayNum])</td> @ <td>%d(nCount)</td> @ <td> @ <div class='statistics-report-graph-line' @ style='width:%d(nSize)%%;'> </div> @ </td> @</tr> } @ </tbody></table> db_finalize(&query); output_table_sorting_javascript("statsTable","ntnx",1); } /* ** Helper for stats_report_by_month_year(), which generates a list of ** week numbers. zTimeframe should be either a timeframe in the form YYYY ** or YYYY-MM. */ static void stats_report_year_weeks(const char *zUserName){ const char *zYear = P("y"); int nYear = zYear ? strlen(zYear) : 0; int i = 0; Stmt qYears = empty_Stmt; char *zDefaultYear = NULL; Blob sql = empty_blob; int nMaxEvents = 1; /* max number of events for all rows. */ int iterations = 0; /* # of active time periods. */ stats_report_init_view(); if(4==nYear){ Blob urlParams = empty_blob; blob_appendf(&urlParams, "y=%T", zYear); stats_report_event_types_menu("byweek", blob_str(&urlParams)); blob_reset(&urlParams); }else{ stats_report_event_types_menu("byweek", NULL); } blob_append(&sql, "SELECT DISTINCT substr(date(mtime),1,4) AS y " "FROM v_reports WHERE 1 ", -1); if(zUserName&&*zUserName){ blob_append_sql(&sql,"AND user=%Q ", zUserName); } blob_append(&sql,"GROUP BY y ORDER BY y", -1); db_prepare(&qYears, "%s", blob_sql_text(&sql)); blob_reset(&sql); cgi_printf("Select year: "); while( SQLITE_ROW == db_step(&qYears) ){ const char *zT = db_column_text(&qYears, 0); if( i++ ){ cgi_printf(" "); } cgi_printf("<a href='?view=byweek&y=%s&type=%c", zT, (char)statsReportType); if(zUserName && *zUserName){ cgi_printf("&user=%t",zUserName); } cgi_printf("'>%s</a>",zT); } db_finalize(&qYears); cgi_printf("<br/>"); if(!zYear || !*zYear){ zDefaultYear = db_text("????", "SELECT strftime('%%Y')"); zYear = zDefaultYear; nYear = 4; } if(4 == nYear){ Stmt stWeek = empty_Stmt; int rowCount = 0; int total = 0; Blob header = empty_blob; blob_appendf(&header, "Timeline events (%s) for the calendar weeks " "of %h", stats_report_label_for_type(), zYear); blob_append_sql(&sql, "SELECT DISTINCT strftime('%%W',mtime) AS wk, " "count(*) AS n " "FROM v_reports " "WHERE %Q=substr(date(mtime),1,4) " "AND mtime < current_timestamp ", zYear); if(zUserName&&*zUserName){ blob_append_sql(&sql, " AND user=%Q ", zUserName); blob_appendf(&header," for user %h", zUserName); } blob_append_sql(&sql, "GROUP BY wk ORDER BY wk DESC"); cgi_printf("<h1>%h</h1>", blob_str(&header)); blob_reset(&header); cgi_printf("<table class='statistics-report-table-events' " "border='0' cellpadding='2' width='100%%' " "cellspacing='0' id='statsTable'>"); cgi_printf("<thead><tr>" "<th>Week</th>" "<th>Events</th>" "<th width='90%%'><!-- relative commits graph --></th>" "</tr></thead>" "<tbody>"); db_prepare(&stWeek, "%s", blob_sql_text(&sql)); blob_reset(&sql); while( SQLITE_ROW == db_step(&stWeek) ){ const int nCount = db_column_int(&stWeek, 1); if(nCount>nMaxEvents){ nMaxEvents = nCount; } ++iterations; } db_reset(&stWeek); while( SQLITE_ROW == db_step(&stWeek) ){ const char *zWeek = db_column_text(&stWeek,0); const int nCount = db_column_int(&stWeek,1); int nSize = nCount ? (int)(100 * nCount / nMaxEvents) : 0; if(!nSize) nSize = 1; total += nCount; cgi_printf("<tr class='row%d'>", ++rowCount % 2 ); cgi_printf("<td><a href='%R/timeline?yw=%t-%s&n=%d&y=%s", zYear, zWeek, nCount, statsReportTimelineYFlag); if(zUserName && *zUserName){ cgi_printf("&u=%t",zUserName); } cgi_printf("'>%s</a></td>",zWeek); cgi_printf("<td>%d</td>",nCount); cgi_printf("<td>"); if(nCount){ cgi_printf("<div class='statistics-report-graph-line'" "style='width:%d%%;'> </div>", nSize); } cgi_printf("</td></tr>"); } db_finalize(&stWeek); free(zDefaultYear); cgi_printf("</tbody></table>"); if(total){ int nAvg = iterations ? (total/iterations) : 0; cgi_printf("<br><div>Total events: %d<br>" "Average per active week: %d</div>", total, nAvg); } output_table_sorting_javascript("statsTable","tnx",-1); } } /* ** WEBPAGE: reports ** ** Shows activity reports for the repository. ** ** Query Parameters: ** ** view=REPORT_NAME Valid values: bymonth, byyear, byuser ** user=NAME Restricts statistics to the given user ** type=TYPE Restricts the report to a specific event type: ** ci (check-in), w (wiki), t (ticket), g (tag) ** Defaulting to all event types. ** ** The view-specific query parameters include: ** ** view=byweek: ** ** y=YYYY The year to report (default is the server's ** current year). */ void stats_report_page(){ HQuery url; /* URL for various branch links */ const char *zView = P("view"); /* Which view/report to show. */ const char *zUserName = P("user"); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } if(!zUserName) zUserName = P("u"); url_initialize(&url, "reports"); if(zUserName && *zUserName){ url_add_parameter(&url,"user", zUserName); statrep_submenu(&url, "(Remove User Flag)", "view", zView, "user"); } statrep_submenu(&url, "By Year", "view", "byyear", 0); statrep_submenu(&url, "By Month", "view", "bymonth", 0); statrep_submenu(&url, "By Week", "view", "byweek", 0); statrep_submenu(&url, "By Weekday", "view", "byweekday", 0); statrep_submenu(&url, "By User", "view", "byuser", "user"); statrep_submenu(&url, "By File", "view", "byfile", "file"); style_submenu_element("Stats", "Stats", "%R/stat"); url_reset(&url); style_header("Activity Reports"); if(0==fossil_strcmp(zView,"byyear")){ stats_report_by_month_year(0, 0, zUserName); }else if(0==fossil_strcmp(zView,"bymonth")){ stats_report_by_month_year(1, 0, zUserName); }else if(0==fossil_strcmp(zView,"byweek")){ stats_report_year_weeks(zUserName); }else if(0==fossil_strcmp(zView,"byuser")){ stats_report_by_user(); }else if(0==fossil_strcmp(zView,"byweekday")){ stats_report_day_of_week(); }else if(0==fossil_strcmp(zView,"byfile")){ stats_report_by_file(); }else{ @ <h1>Activity Reports:</h1> @ <ul> @ <li>%z(href("?view=byyear"))Events by year</a></li> @ <li>%z(href("?view=bymonth"))Events by month</a></li> @ <li>%z(href("?view=byweek"))Events by calendar week</a></li> @ <li>%z(href("?view=byweekday"))Events by day of the week</a></li> @ <li>%z(href("?view=byuser"))Events by user</a></li> @ <li>%z(href("?view=byfile"))Events by file</a></li> @ </ul> } style_footer(); } |
Changes to src/style.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement the basic web page look and feel. ** */ #include "config.h" #include "style.h" /* ** Elements of the submenu are collected into the following | > | > > | > > > > | > > | | | > > > > > > > > > > > > > > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement the basic web page look and feel. ** */ #include "VERSION.h" #include "config.h" #include "style.h" /* ** Elements of the submenu are collected into the following ** structure and displayed below the main menu. ** ** Populate these structure with calls to ** ** style_submenu_element() ** style_submenu_entry() ** style_submenu_checkbox() ** style_submenu_multichoice() ** ** prior to calling style_footer(). The style_footer() routine ** will generate the appropriate HTML text just below the main ** menu. */ static struct Submenu { const char *zLabel; /* Button label */ const char *zTitle; const char *zLink; /* Jump to this link when button is pressed */ } aSubmenu[30]; static int nSubmenu = 0; /* Number of buttons */ static struct SubmenuCtrl { const char *zName; /* Form query parameter */ const char *zLabel; /* Label. Might be NULL for FF_MULTI */ unsigned char eType; /* FF_ENTRY, FF_MULTI, FF_BINARY */ unsigned char isDisabled; /* True if this control is grayed out */ short int iSize; /* Width for FF_ENTRY. Count for FF_MULTI */ const char **azChoice; /* value/display pairs for FF_MULTI */ const char *zFalse; /* FF_BINARY label when false */ } aSubmenuCtrl[20]; static int nSubmenuCtrl = 0; #define FF_ENTRY 1 #define FF_MULTI 2 #define FF_BINARY 3 /* ** Remember that the header has been generated. The footer is omitted ** if an error occurs before the header. */ static int headerHasBeenGenerated = 0; /* ** remember, if a sidebox was used */ static int sideboxUsed = 0; /* ** Ad-unit styles. */ static unsigned adUnitFlags = 0; /* ** List of hyperlinks and forms that need to be resolved by javascript in ** the footer. */ char **aHref = 0; int nHref = 0; |
︙ | ︙ | |||
161 162 163 164 165 166 167 | ** Generate javascript that will set the href= attribute on all anchors. */ void style_resolve_href(void){ int i; int nDelay = db_get_int("auto-hyperlink-delay",10); if( !g.perm.Hyperlink ) return; if( nHref==0 && nFormAction==0 ) return; | | < | > > > > | | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | ** Generate javascript that will set the href= attribute on all anchors. */ void style_resolve_href(void){ int i; int nDelay = db_get_int("auto-hyperlink-delay",10); if( !g.perm.Hyperlink ) return; if( nHref==0 && nFormAction==0 ) return; @ <script> @ function setAllHrefs(){ if( g.javascriptHyperlink ){ for(i=0; i<nHref; i++){ @ gebi("a%d(i+1)").href="%s(aHref[i])"; } } for(i=0; i<nFormAction; i++){ @ gebi("form%d(i+1)").action="%s(aFormAction[i])"; } @ } if( sqlite3_strglob("*Opera Mini/[1-9]*", P("HTTP_USER_AGENT"))==0 ){ /* Special case for Opera Mini, which executes JS server-side */ @ var isOperaMini = Object.prototype.toString.call(window.operamini) @ === "[object OperaMini]"; @ if( isOperaMini ){ @ setTimeout("setAllHrefs();",%d(nDelay)); @ } }else if( db_get_boolean("auto-hyperlink-ishuman",0) && g.isHuman ){ /* Active hyperlinks after a delay */ @ setTimeout("setAllHrefs();",%d(nDelay)); }else if( db_get_boolean("auto-hyperlink-mouseover",0) ){ /* Require mouse movement before starting the teim that will ** activating hyperlinks */ @ document.getElementsByTagName("body")[0].onmousemove=function(){ @ setTimeout("setAllHrefs();",%d(nDelay)); @ this.onmousemove = null; @ } }else{ /* Active hyperlinks after a delay */ @ setTimeout("setAllHrefs();",%d(nDelay)); } @ </script> } /* ** Add a new element to the submenu */ void style_submenu_element( const char *zLabel, const char *zTitle, const char *zLink, ... ){ va_list ap; assert( nSubmenu < sizeof(aSubmenu)/sizeof(aSubmenu[0]) ); aSubmenu[nSubmenu].zLabel = zLabel; aSubmenu[nSubmenu].zTitle = zTitle ? zTitle : zLabel; va_start(ap, zLink); aSubmenu[nSubmenu].zLink = vmprintf(zLink, ap); va_end(ap); nSubmenu++; } void style_submenu_entry( const char *zName, /* Query parameter name */ const char *zLabel, /* Label before the entry box */ int iSize, /* Size of the entry box */ int isDisabled /* True if disabled */ ){ assert( nSubmenuCtrl < ArraySize(aSubmenuCtrl) ); aSubmenuCtrl[nSubmenuCtrl].zName = zName; aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel; aSubmenuCtrl[nSubmenuCtrl].iSize = iSize; aSubmenuCtrl[nSubmenuCtrl].isDisabled = isDisabled; aSubmenuCtrl[nSubmenuCtrl].eType = FF_ENTRY; nSubmenuCtrl++; } void style_submenu_binary( const char *zName, /* Query parameter name */ const char *zTrue, /* Label to show when parameter is true */ const char *zFalse, /* Label to show when the parameter is false */ int isDisabled /* True if this control is disabled */ ){ assert( nSubmenuCtrl < ArraySize(aSubmenuCtrl) ); aSubmenuCtrl[nSubmenuCtrl].zName = zName; aSubmenuCtrl[nSubmenuCtrl].zLabel = zTrue; aSubmenuCtrl[nSubmenuCtrl].zFalse = zFalse; aSubmenuCtrl[nSubmenuCtrl].isDisabled = isDisabled; aSubmenuCtrl[nSubmenuCtrl].eType = FF_BINARY; nSubmenuCtrl++; } void style_submenu_multichoice( const char *zName, /* Query parameter name */ int nChoice, /* Number of options */ const char **azChoice, /* value/display pairs. 2*nChoice entries */ int isDisabled /* True if this control is disabled */ ){ assert( nSubmenuCtrl < ArraySize(aSubmenuCtrl) ); aSubmenuCtrl[nSubmenuCtrl].zName = zName; aSubmenuCtrl[nSubmenuCtrl].iSize = nChoice; aSubmenuCtrl[nSubmenuCtrl].azChoice = azChoice; aSubmenuCtrl[nSubmenuCtrl].isDisabled = isDisabled; aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI; nSubmenuCtrl++; } /* ** Compare two submenu items for sorting purposes */ static int submenuCompare(const void *a, const void *b){ const struct Submenu *A = (const struct Submenu*)a; const struct Submenu *B = (const struct Submenu*)b; |
︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 | }else{ va_list ap; va_start(ap, zFormat); local_zCurrentPage = vmprintf(zFormat, ap); va_end(ap); } } /* ** Draw the header. */ void style_header(const char *zTitleFormat, ...){ va_list ap; char *zTitle; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > | | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | }else{ va_list ap; va_start(ap, zFormat); local_zCurrentPage = vmprintf(zFormat, ap); va_end(ap); } } /* ** Create a TH1 variable containing the URL for the specified config resource. ** The resulting variable name will be of the form $[zVarPrefix]_url. */ static void url_var( const char *zVarPrefix, const char *zConfigName, const char *zPageName ){ char *zVarName = mprintf("%s_url", zVarPrefix); char *zUrl = mprintf("%s/%s?id=%x", g.zTop, zPageName, skin_id(zConfigName)); Th_Store(zVarName, zUrl); free(zUrl); free(zVarName); } /* ** Create a TH1 variable containing the URL for the specified config image. ** The resulting variable name will be of the form $[zImageName]_image_url. */ static void image_url_var(const char *zImageName){ char *zVarPrefix = mprintf("%s_image", zImageName); char *zConfigName = mprintf("%s-image", zImageName); url_var(zVarPrefix, zConfigName, zImageName); free(zVarPrefix); free(zConfigName); } /* ** Draw the header. */ void style_header(const char *zTitleFormat, ...){ va_list ap; char *zTitle; const char *zHeader = skin_get("header"); login_check_credentials(); va_start(ap, zTitleFormat); zTitle = vmprintf(zTitleFormat, ap); va_end(ap); cgi_destination(CGI_HEADER); @ <!DOCTYPE html> if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1); /* Generate the header up through the main menu */ Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); Th_Store("title", zTitle); Th_Store("baseurl", g.zBaseURL); Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL); Th_Store("home", g.zTop); Th_Store("index_page", db_get("index-page","/home")); if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath); Th_Store("current_page", local_zCurrentPage); Th_Store("csrf_token", g.zCsrfToken); Th_Store("release_version", RELEASE_VERSION); Th_Store("manifest_version", MANIFEST_VERSION); Th_Store("manifest_date", MANIFEST_DATE); Th_Store("compiler_name", COMPILER_NAME); url_var("stylesheet", "css", "style.css"); image_url_var("logo"); image_url_var("background"); if( !login_is_nobody() ){ Th_Store("login", g.zLogin); } if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br />\n", -1); Th_Render(zHeader); if( g.thTrace ) Th_Trace("END_HEADER<br />\n", -1); Th_Unstore("title"); /* Avoid collisions with ticket field names */ cgi_destination(CGI_BODY); |
︙ | ︙ | |||
296 297 298 299 300 301 302 | ** Maintenance note: this function must of course be available ** before it is called. It "should" go in the HEAD so that client ** HEAD code can make use of it, but because the client can replace ** the HEAD, and some fossil pages rely on gebi(), we put it here. */ @ <script> @ function gebi(x){ | | | > > > > > > > | | > > > > > > > > > > > | > > | > | | > | > > > > > > | | > > > > | > > > > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > | | | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 | ** Maintenance note: this function must of course be available ** before it is called. It "should" go in the HEAD so that client ** HEAD code can make use of it, but because the client can replace ** the HEAD, and some fossil pages rely on gebi(), we put it here. */ @ <script> @ function gebi(x){ @ if(x.substr(0,1)=='#') x = x.substr(1); @ var e = document.getElementById(x); @ if(!e) throw new Error('Expecting element with ID '+x); @ else return e;} @ </script> } #if INTERFACE /* Allowed parameters for style_adunit() */ #define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ #define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */ #endif /* ** Various page implementations can invoke this interface to let the ** style manager know what kinds of ads are appropriate for this page. */ void style_adunit_config(unsigned int mFlags){ adUnitFlags = mFlags; } /* ** Return the text of an ad-unit, if one should be rendered. Return ** NULL if no ad-unit is desired. ** ** The *pAdFlag value might be set to ADUNIT_RIGHT_OK if this is ** a right-hand vertical ad. */ static const char *style_adunit_text(unsigned int *pAdFlag){ const char *zAd = 0; *pAdFlag = 0; if( adUnitFlags & ADUNIT_OFF ) return 0; /* Disallow ads on this page */ if( g.perm.Admin && db_get_boolean("adunit-omit-if-admin",0) ){ return 0; } if( !login_is_nobody() && fossil_strcmp(g.zLogin,"anonymous")!=0 && db_get_boolean("adunit-omit-if-user",0) ){ return 0; } if( (adUnitFlags & ADUNIT_RIGHT_OK)!=0 && !fossil_all_whitespace(zAd = db_get("adunit-right", 0)) && !cgi_body_contains("<table") ){ *pAdFlag = ADUNIT_RIGHT_OK; return zAd; }else if( !fossil_all_whitespace(zAd = db_get("adunit",0)) ){ return zAd; } return 0; } /* ** Draw the footer at the bottom of the page. */ void style_footer(void){ const char *zFooter; const char *zAd = 0; unsigned int mAdFlags = 0; if( !headerHasBeenGenerated ) return; /* Go back and put the submenu at the top of the page. We delay the ** creation of the submenu until the end so that we can add elements ** to the submenu while generating page text. */ cgi_destination(CGI_HEADER); if( nSubmenu+nSubmenuCtrl>0 ){ int i; if( nSubmenuCtrl ){ cgi_printf("<form id='f01' method='GET' action='%R/%s'>", g.zPath); } @ <div class="submenu"> if( nSubmenu>0 ){ qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare); for(i=0; i<nSubmenu; i++){ struct Submenu *p = &aSubmenu[i]; if( p->zLink==0 ){ @ <span class="label">%h(p->zLabel)</span> }else{ @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a> } } } if( nSubmenuCtrl>0 ){ for(i=0; i<nSubmenuCtrl; i++){ const char *zQPN = aSubmenuCtrl[i].zName; const char *zDisabled = " disabled"; if( !aSubmenuCtrl[i].isDisabled ){ zDisabled = ""; cgi_tag_query_parameter(zQPN); } switch( aSubmenuCtrl[i].eType ){ case FF_ENTRY: { cgi_printf( "<span class='submenuctrl'>" " %h<input type='text' name='%s' size='%d' maxlength='%d'" " value='%h'%s></span>\n", aSubmenuCtrl[i].zLabel, zQPN, aSubmenuCtrl[i].iSize, aSubmenuCtrl[i].iSize, PD(zQPN,""), zDisabled ); break; } case FF_MULTI: { int j; const char *zVal = P(zQPN); cgi_printf( "<select class='submenuctrl' size='1' name='%s'%s " "onchange='gebi(\"f01\").submit();'>\n", zQPN, zDisabled ); for(j=0; j<aSubmenuCtrl[i].iSize*2; j+=2){ const char *zQPV = aSubmenuCtrl[i].azChoice[j]; cgi_printf( "<option value='%h'%s>%h</option>\n", zQPV, fossil_strcmp(zVal,zQPV)==0 ? " selected" : "", aSubmenuCtrl[i].azChoice[j+1] ); } @ </select> break; } case FF_BINARY: { int isTrue = PB(zQPN); cgi_printf( "<select class='submenuctrl' size='1' name='%s'%s " "onchange='gebi(\"f01\").submit();'>\n", zQPN, zDisabled ); cgi_printf( "<option value='1'%s>%h</option>\n", isTrue ? " selected":"", aSubmenuCtrl[i].zLabel ); cgi_printf( "<option value='0'%s>%h</option>\n", (!isTrue) ? " selected":"", aSubmenuCtrl[i].zFalse ); @ </select> break; } } } } @ </div> if( nSubmenuCtrl ){ cgi_query_parameters_to_hidden(); cgi_tag_query_parameter(0); @ </form> } } zAd = style_adunit_text(&mAdFlags); if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){ @ <div class="content adunit_right_container"> @ <div class="adunit_right"> cgi_append_content(zAd, -1); @ </div> }else{ if( zAd ){ @ <div class="adunit_banner"> cgi_append_content(zAd, -1); @ </div> } @ <div class="content"> } cgi_destination(CGI_BODY); if( sideboxUsed ){ /* Put the footer at the bottom of the page. ** the additional clear/both is needed to extend the content ** part to the end of an optional sidebox. */ @ <div class="endContent"></div> } @ </div> /* Set the href= field on hyperlinks. Do this before the footer since ** the footer will be generating </html> */ style_resolve_href(); zFooter = skin_get("footer"); if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1); Th_Render(zFooter); if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1); /* Render trace log if TH1 tracing is enabled. */ if( g.thTrace ){ cgi_append_content("<span class=\"thTrace\"><hr />\n", -1); |
︙ | ︙ | |||
393 394 395 396 397 398 399 | /* End the side-box */ void style_sidebox_end(void){ @ </div> } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < < < < | 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 | /* End the side-box */ void style_sidebox_end(void){ @ </div> } /* The following table contains bits of default CSS that must ** be included if they are not found in the application-defined ** CSS. */ const struct strctCssDefaults { const char *elementClass; /* Name of element needed */ const char *comment; /* Comment text */ const char *value; /* CSS text */ } cssDefaultList[] = { { "div.sidebox", "The nomenclature sidebox for branches,..", @ float: right; @ background-color: white; @ border-width: medium; @ border-style: double; @ margin: 10px; |
︙ | ︙ | |||
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 | "The suppressed duplicates lines in timeline, ..", @ font-style: italic; @ font-size: small; }, { "table.timelineTable", "the format for the timeline data table", @ border: 0; }, { "td.timelineTableCell", "the format for the timeline data cells", @ vertical-align: top; @ text-align: left; }, { "tr.timelineCurrent td.timelineTableCell", "the format for the timeline data cell of the current checkout", @ padding: .1em .2em; @ border: 1px dashed #446979; }, { "span.timelineLeaf", "the format for the timeline leaf marks", @ font-weight: bold; }, { "a.timelineHistLink", "the format for the timeline version links", @ }, { "span.timelineHistDsp", "the format for the timeline version display(no history permission!)", @ font-weight: bold; }, { "td.timelineTime", "the format for the timeline time display", @ vertical-align: top; @ text-align: right; }, { "td.timelineGraph", "the format for the grap placeholder cells in timelines", @ width: 20px; @ text-align: left; @ vertical-align: top; }, | > > > > > > > > > > > > > | 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 | "The suppressed duplicates lines in timeline, ..", @ font-style: italic; @ font-size: small; }, { "table.timelineTable", "the format for the timeline data table", @ border: 0; @ border-collapse: collapse; }, { "td.timelineTableCell", "the format for the timeline data cells", @ vertical-align: top; @ text-align: left; }, { "tr.timelineCurrent td.timelineTableCell", "the format for the timeline data cell of the current checkout", @ padding: .1em .2em; @ border: 1px dashed #446979; }, { "tr.timelineSelected", "The row in the timeline table that contains the entry of interest", @ padding: .1em .2em; @ border: 2px solid lightgray; @ background-color: #ffc; @ box-shadow: 4px 4px 2px #888; }, { "tr.timelineSpacer", "An extra row inserted to give vertical space between two rows", @ height: 1ex; }, { "span.timelineLeaf", "the format for the timeline leaf marks", @ font-weight: bold; }, { "a.timelineHistLink", "the format for the timeline version links", @ }, { "span.timelineHistDsp", "the format for the timeline version display(no history permission!)", @ font-weight: bold; }, { "td.timelineTime", "the format for the timeline time display", @ vertical-align: top; @ text-align: right; @ white-space: nowrap; }, { "td.timelineGraph", "the format for the grap placeholder cells in timelines", @ width: 20px; @ text-align: left; @ vertical-align: top; }, |
︙ | ︙ | |||
728 729 730 731 732 733 734 735 | @ border: 0; }, { "td.browser", "format for cells in the file browser", @ width: 24%; @ vertical-align: top; }, { "ul.browser", | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | @ border: 0; }, { "td.browser", "format for cells in the file browser", @ width: 24%; @ vertical-align: top; }, { ".filetree", "tree-view file browser", @ margin: 1em 0; @ line-height: 1.5; }, { ".filetree > ul", "tree-view top-level list", @ display: inline-block; }, { ".filetree ul", "tree-view lists", @ margin: 0; @ padding: 0; @ list-style: none; }, { ".filetree ul.collapsed", "tree-view collapsed list", @ display: none; }, { ".filetree ul ul", "tree-view lists below the root", @ position: relative; @ margin: 0 0 0 21px; }, { ".filetree li", "tree-view lists items", @ position: relative; @ margin: 0; @ padding: 0; }, { ".filetree li li:before", "tree-view node lines", @ content: ''; @ position: absolute; @ top: -.8em; @ left: -14px; @ width: 14px; @ height: 1.5em; @ border-left: 2px solid #aaa; @ border-bottom: 2px solid #aaa; }, { ".filetree li > ul:before", "tree-view directory lines", @ content: ''; @ position: absolute; @ top: -1.5em; @ bottom: 0; @ left: -35px; @ border-left: 2px solid #aaa; }, { ".filetree li.last > ul:before", "hide lines for last-child directories", @ display: none; }, { ".filetree a", "tree-view links", " position: relative;\n" " z-index: 1;\n" " display: table-cell;\n" " min-height: 16px;\n" " padding-left: 21px;\n" " background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP" "\\/\\/\\/yEhIf\\/\\/\\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfmg" "OUvoaqDSCxrEEfF14GqFXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw==);\n" " background-position: center left;\n" " background-repeat: no-repeat;\n" }, { "ul.browser", "list of files in the 'flat-view' file browser", @ list-style-type: none; @ padding: 10px; @ margin: 0px; @ white-space: nowrap; }, { "ul.browser li.file", "List element in the 'flat-view' file browser for a file", " background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP" "\\/\\/\\/yEhIf\\/\\/\\/wAAACH5BAEHAAIALAAAAAAQABAAAAIvlIKpxqcfm" "gOUvoaqDSCxrEEfF14GqFXImJZsu73wepJzVMNxrtNTj3NATMKhpwAAOw==);\n" " background-repeat: no-repeat;\n" " background-position: 0px center;\n" " padding-left: 20px;\n" " padding-top: 2px;\n" }, { "ul.browser li.dir", "List element in the 'flat-view file browser for a directory", " background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIi" "Iv\\/\\/\\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaX" "po+jUs6b5Z/K4siDu5RPUFADs=);\n" " background-repeat: no-repeat;\n" " background-position: 0px center;\n" " padding-left: 20px;\n" " padding-top: 2px;\n" }, { "div.filetreeline", "line of a file tree", @ display: table; @ width: 100%; @ white-space: nowrap; }, { ".filetree .dir > div.filetreeline > a", "tree-view directory links", " background-image: url(data:image/gif;base64,R0lGODlhEAAQAJEAAP/WVCIi" "Iv\\/\\/\\/wAAACH5BAEHAAIALAAAAAAQABAAAAInlI9pwa3XYniCgQtkrAFfLXkiFo1jaXp" "o+jUs6b5Z/K4siDu5RPUFADs=);\n" }, { "div.filetreeage", "Last change floating display on the right", @ display: table-cell; @ padding-left: 3em; @ text-align: right; }, { "div.filetreeline:hover", "Highlight the line of a file tree", @ background-color: #eee; }, { "table.login_out", "table format for login/out label/input table", @ text-align: left; @ margin-right: 10px; @ margin-left: 10px; @ margin-top: 10px; |
︙ | ︙ | |||
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 | @ text-align: right; @ vertical-align: top; @ white-space: nowrap; }, { "span.ueditInheritNobody", "color for capabilities, inherited by nobody", @ color: green; }, { "span.ueditInheritDeveloper", "color for capabilities, inherited by developer", @ color: red; }, { "span.ueditInheritReader", "color for capabilities, inherited by reader", @ color: black; }, { "span.ueditInheritAnonymous", "color for capabilities, inherited by anonymous", @ color: blue; }, { "span.capability", "format for capabilities, mentioned on the user edit page", @ font-weight: bold; }, { "span.usertype", "format for different user types, mentioned on the user edit page", | > > > > | 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 | @ text-align: right; @ vertical-align: top; @ white-space: nowrap; }, { "span.ueditInheritNobody", "color for capabilities, inherited by nobody", @ color: green; @ padding: .2em; }, { "span.ueditInheritDeveloper", "color for capabilities, inherited by developer", @ color: red; @ padding: .2em; }, { "span.ueditInheritReader", "color for capabilities, inherited by reader", @ color: black; @ padding: .2em; }, { "span.ueditInheritAnonymous", "color for capabilities, inherited by anonymous", @ color: blue; @ padding: .2em; }, { "span.capability", "format for capabilities, mentioned on the user edit page", @ font-weight: bold; }, { "span.usertype", "format for different user types, mentioned on the user edit page", |
︙ | ︙ | |||
913 914 915 916 917 918 919 | { "td.rpteditex", "format for example table cells on the report edit page", @ border-width: thin; @ border-color: #000000; @ border-style: solid; }, { "input.checkinUserColor", | | | 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | { "td.rpteditex", "format for example table cells on the report edit page", @ border-width: thin; @ border-color: #000000; @ border-style: solid; }, { "input.checkinUserColor", "format for user color input on check-in edit page", @ /* no special definitions, class defined, to enable color pickers, f.e.: @ ** add the color picker found at http:jscolor.com as java script include @ ** to the header and configure the java script file with @ ** 1. use as bindClass :checkinUserColor @ ** 2. change the default hash adding behaviour to ON @ ** or change the class defition of element identified by id="clrcust" @ ** to a standard jscolor definition with java script in the footer. */ |
︙ | ︙ | |||
976 977 978 979 980 981 982 983 984 985 986 987 988 989 | @ color: red; }, { "ul.filelist", "List of files in a timeline", @ margin-top: 3px; @ line-height: 100%; }, { "table.sbsdiffcols", "side-by-side diff display (column-based)", @ width: 90%; @ border-spacing: 0; @ font-size: xx-small; }, { "table.sbsdiffcols td", | > > > > | 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 | @ color: red; }, { "ul.filelist", "List of files in a timeline", @ margin-top: 3px; @ line-height: 100%; }, { "ul.filelist li", "List of files in a timeline", @ padding-top: 1px; }, { "table.sbsdiffcols", "side-by-side diff display (column-based)", @ width: 90%; @ border-spacing: 0; @ font-size: xx-small; }, { "table.sbsdiffcols td", |
︙ | ︙ | |||
1049 1050 1051 1052 1053 1054 1055 | }, { "pre.th1error", "format for th1 script errors", @ white-space: pre-wrap; @ word-wrap: break-word; @ color: red; }, | | | < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > > > | > > > > > > > > | | > | > > > > > > > | > | > > > | | | < | < > > | > > > > > > > > > > | 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 | }, { "pre.th1error", "format for th1 script errors", @ white-space: pre-wrap; @ word-wrap: break-word; @ color: red; }, { "table.label-value th", "The label/value pairs on (for example) the ci page", @ vertical-align: top; @ text-align: right; @ padding: 0.2ex 2ex; }, { ".statistics-report-graph-line", "for the /reports views", @ background-color: #446979; }, { ".statistics-report-table-events th", "", @ padding: 0 1em 0 1em; }, { ".statistics-report-table-events td", "", @ padding: 0.1em 1em 0.1em 1em; }, { ".statistics-report-row-year", "", @ text-align: left; }, { ".statistics-report-week-number-label", "for the /stats_report views", @ text-align: right; @ font-size: 0.8em; }, { ".statistics-report-week-of-year-list", "for the /stats_report views", @ font-size: 0.8em; }, { "tr.row0", "even table row color", @ /* use default */ }, { "tr.row1", "odd table row color", @ /* Use default */ }, { "#usetupEditCapability", "format for capabilities string, mentioned on the user edit page", @ font-weight: bold; }, { "#canvas", "timeline graph node colors", @ color: black; @ background-color: white; }, { "table.adminLogTable", "Class for the /admin_log table", @ text-align: left; }, { ".adminLogTable .adminTime", "Class for the /admin_log table", @ text-align: left; @ vertical-align: top; @ white-space: nowrap; }, { ".fileage table", "The fileage table", @ border-spacing: 0; }, { ".fileage tr:hover", "Mouse-over effects for the file-age table", @ background-color: #eee; }, { ".fileage td", "fileage table cells", @ vertical-align: top; @ text-align: left; @ border-top: 1px solid #ddd; @ padding-top: 3px; }, { ".fileage td:first-child", "fileage first column (the age)", @ white-space: nowrap; }, { ".fileage td:nth-child(2)", "fileage second column (the filename)", @ padding-left: 1em; @ padding-right: 1em; }, { ".fileage td:nth-child(3)", "fileage third column (the check-in comment)", @ word-wrap: break-word; @ max-width: 50%; }, { ".brlist table", "The list of branches", @ border-spacing: 0; }, { ".brlist table th", "Branch list table headers", @ text-align: left; @ padding: 0px 1em 0.5ex 0px; }, { ".brlist table td", "Branch list table headers", @ padding: 0px 2em 0px 0px; @ white-space: nowrap; }, { "th.sort:after", "General styles for sortable column marker", @ margin-left: .4em; @ cursor: pointer; @ text-shadow: 0 0 0 #000; /* Makes arrow darker */ }, { "th.sort.none:after", "None sort column marker", @ content: '\2666'; }, { "th.sort.asc:after", "Ascending sort column marker", @ content: '\2193'; }, { "th.sort.desc:after", "Descending sort column marker", @ content: '\2191'; }, { "span.snippet>mark", "Search markup", @ background-color: inherit; @ font-weight: bold; }, { "div.searchForm", "Container for the search terms entry box", @ text-align: center; }, { "p.searchEmpty", "Message explaining that there are no search results", @ font-style: italic; }, { 0, 0, 0 } }; /* ** Append all of the default CSS to the CGI output. */ void cgi_append_default_css(void) { int i; cgi_printf("%s", builtin_text("skins/default/css.txt")); for( i=0; cssDefaultList[i].elementClass; i++ ){ if( cssDefaultList[i].elementClass[0] ){ cgi_printf("/* %s */\n%s {\n%s\n}\n\n", cssDefaultList[i].comment, cssDefaultList[i].elementClass, cssDefaultList[i].value ); } } } /* ** Search string zHaystack for zNeedle. zNeedle must be an isolated ** word with space or punctuation on either size. ** ** Return true if found. Return false if not found */ static int containsString(const char *zHaystack, const char *zNeedle){ char *z; int n; while( zHaystack[0] ){ z = strstr(zHaystack, zNeedle); if( z==0 ) return 0; n = (int)strlen(zNeedle); if( (z==zHaystack || !fossil_isalnum(z[-1])) && !fossil_isalnum(z[n]) ){ return 1; } zHaystack = z + n; } return 0; } /* ** WEBPAGE: style.css */ void page_style_css(void){ Blob css; int i; cgi_set_content_type("text/css"); blob_init(&css,skin_get("css"),-1); /* add special missing definitions */ for(i=1; cssDefaultList[i].elementClass; i++){ char *z = blob_str(&css); if( !containsString(z, cssDefaultList[i].elementClass) ){ blob_appendf(&css, "/* %s */\n%s {\n%s}\n", cssDefaultList[i].comment, cssDefaultList[i].elementClass, cssDefaultList[i].value); } } /* Process through TH1 in order to give an opportunity to substitute ** variables such as $baseurl. */ Th_Store("baseurl", g.zBaseURL); Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL); Th_Store("home", g.zTop); image_url_var("logo"); image_url_var("background"); Th_Render(blob_str(&css)); /* Tell CGI that the content returned by this page is considered cacheable */ g.isConst = 1; } /* ** WEBPAGE: test_env */ void page_test_env(void){ char c; int i; int showAll; char zCap[30]; static const char *const azCgiVars[] = { "COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", "HTTP_HOST", "HTTP_USER_AGENT", "HTTP_REFERER", "PATH_INFO", "PATH_TRANSLATED", "QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_PROTOCOL", }; login_check_credentials(); if( !g.perm.Admin && !g.perm.Setup && !db_get_boolean("test_env_enable",0) ){ login_needed(0); return; } for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]); style_header("Environment Test"); showAll = atoi(PD("showall","0")); if( !showAll ){ style_submenu_element("Show Cookies", 0, "%R/test_env?showall=1"); }else{ style_submenu_element("Hide Cookies", 0, "%R/test_env"); } #if !defined(_WIN32) @ uid=%d(getuid()), gid=%d(getgid())<br /> #endif @ g.zBaseURL = %h(g.zBaseURL)<br /> @ g.zHttpsURL = %h(g.zHttpsURL)<br /> @ g.zTop = %h(g.zTop)<br /> @ g.zPath = %h(g.zPath)<br /> for(i=0, c='a'; c<='z'; c++){ if( login_has_capability(&c, 1, 0) ) zCap[i++] = c; } zCap[i] = 0; @ g.userUid = %d(g.userUid)<br /> @ g.zLogin = %h(g.zLogin)<br /> @ g.isHuman = %d(g.isHuman)<br /> @ capabilities = %s(zCap)<br /> for(i=0, c='a'; c<='z'; c++){ if( login_has_capability(&c, 1, LOGIN_ANON) && !login_has_capability(&c, 1, 0) ) zCap[i++] = c; } zCap[i] = 0; if( i>0 ){ @ anonymous-adds = %s(zCap)<br /> } @ g.zRepositoryName = %h(g.zRepositoryName)<br /> @ load_average() = %f(load_average())<br /> @ <hr> P("HTTP_USER_AGENT"); cgi_print_all(showAll); if( showAll && blob_size(&g.httpHeader)>0 ){ @ <hr> @ <pre> @ %h(blob_str(&g.httpHeader)) |
︙ | ︙ | |||
1223 1224 1225 1226 1227 1228 1229 | /* ** This page is a honeypot for spiders and bots. ** ** WEBPAGE: honeypot */ void honeypot_page(void){ cgi_set_status(403, "Forbidden"); | | | 1470 1471 1472 1473 1474 1475 1476 1477 1478 | /* ** This page is a honeypot for spiders and bots. ** ** WEBPAGE: honeypot */ void honeypot_page(void){ cgi_set_status(403, "Forbidden"); @ <p>Please enable javascript or log in to see this content</p> } |
Changes to src/sync.c.
︙ | ︙ | |||
46 47 48 49 50 51 52 | if( is_false(zAutosync) ){ return 0; /* Autosync is completely off */ } }else{ /* Autosync defaults on. To make it default off, "return" here. */ } url_parse(0, URL_REMEMBER); | | | | > > > > | | > > > > > > > > > > > > > > > > | > > > > > > > > > > > | | | | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | if( is_false(zAutosync) ){ return 0; /* Autosync is completely off */ } }else{ /* Autosync defaults on. To make it default off, "return" here. */ } url_parse(0, URL_REMEMBER); if( g.url.protocol==0 ) return 0; if( g.url.user!=0 && g.url.passwd==0 ){ g.url.passwd = unobscure(db_get("last-sync-pw", 0)); g.url.flags |= URL_PROMPT_PW; url_prompt_for_password(); } g.zHttpAuth = get_httpauth(); url_remember(); #if 0 /* Disabled for now */ if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ /* When doing an automatic pull, also automatically pull shuns from ** the server if pull_shuns is enabled. ** ** TODO: What happens if the shun list gets really big? ** Maybe the shunning list should only be pulled on every 10th ** autosync, or something? */ configSync = CONFIGSET_SHUN; } #endif if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE; fossil_print("Autosync: %s\n", g.url.canonical); url_enable_proxy("via proxy: "); rc = client_sync(flags, configSync, 0); return rc; } /* ** This routine will try a number of times to perform autosync with a ** 0.5 second sleep between attempts; returning the last autosync status. */ int autosync_loop(int flags, int nTries){ int n = 0; int rc = 0; while( (n==0 || n<nTries) && (rc=autosync(flags)) ){ if( rc ){ if( ++n<nTries ){ fossil_warning("Autosync failed, making another attempt."); sqlite3_sleep(500); }else{ fossil_warning("Autosync failed."); } } } return rc; } /* ** This routine processes the command-line argument for push, pull, ** and sync. If a command-line argument is given, that is the URL ** of a server to sync against. If no argument is given, use the ** most recently synced URL. Remember the current URL for next time. */ static void process_sync_args(unsigned *pConfigFlags, unsigned *pSyncFlags){ const char *zUrl = 0; const char *zHttpAuth = 0; unsigned configSync = 0; unsigned urlFlags = URL_REMEMBER | URL_PROMPT_PW; int urlOptional = 0; if( find_option("autourl",0,0)!=0 ){ urlOptional = 1; urlFlags = 0; } zHttpAuth = find_option("httpauth","B",1); if( find_option("once",0,0)!=0 ) urlFlags &= ~URL_REMEMBER; if( find_option("private",0,0)!=0 ){ *pSyncFlags |= SYNC_PRIVATE; } if( find_option("verbose","v",0)!=0 ){ *pSyncFlags |= SYNC_VERBOSE; } /* The --verily option to sync, push, and pull forces extra igot cards ** to be exchanged. This can overcome malfunctions in the sync protocol. */ if( find_option("verily",0,0)!=0 ){ *pSyncFlags |= SYNC_RESYNC; } url_proxy_options(); clone_ssh_find_options(); db_find_and_open_repository(0, 0); db_open_config(0); if( g.argc==2 ){ if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN; }else if( g.argc==3 ){ zUrl = g.argv[2]; } if( urlFlags & URL_REMEMBER ){ clone_ssh_db_set_options(); } url_parse(zUrl, urlFlags); remember_or_get_http_auth(zHttpAuth, urlFlags & URL_REMEMBER, zUrl); url_remember(); if( g.url.protocol==0 ){ if( urlOptional ) fossil_exit(0); usage("URL"); } user_select(); if( g.argc==2 ){ if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){ fossil_print("Sync with %s\n", g.url.canonical); }else if( (*pSyncFlags) & SYNC_PUSH ){ fossil_print("Push to %s\n", g.url.canonical); }else if( (*pSyncFlags) & SYNC_PULL ){ fossil_print("Pull from %s\n", g.url.canonical); } } url_enable_proxy("via proxy: "); *pConfigFlags |= configSync; } /* |
︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 | ** ** See also: clone, push, sync, remote-url */ void pull_cmd(void){ unsigned configFlags = 0; unsigned syncFlags = SYNC_PULL; process_sync_args(&configFlags, &syncFlags); client_sync(syncFlags, configFlags, 0); } /* ** COMMAND: push ** ** Usage: %fossil push ?URL? ?options? | > > > > | 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | ** ** See also: clone, push, sync, remote-url */ void pull_cmd(void){ unsigned configFlags = 0; unsigned syncFlags = SYNC_PULL; process_sync_args(&configFlags, &syncFlags); /* We should be done with options.. */ verify_all_options(); client_sync(syncFlags, configFlags, 0); } /* ** COMMAND: push ** ** Usage: %fossil push ?URL? ?options? |
︙ | ︙ | |||
184 185 186 187 188 189 190 191 192 193 194 195 196 197 | ** ** See also: clone, pull, sync, remote-url */ void push_cmd(void){ unsigned configFlags = 0; unsigned syncFlags = SYNC_PUSH; process_sync_args(&configFlags, &syncFlags); if( db_get_boolean("dont-push",0) ){ fossil_fatal("pushing is prohibited: the 'dont-push' option is set"); } client_sync(syncFlags, 0, 0); } | > > > > | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | ** ** See also: clone, pull, sync, remote-url */ void push_cmd(void){ unsigned configFlags = 0; unsigned syncFlags = SYNC_PUSH; process_sync_args(&configFlags, &syncFlags); /* We should be done with options.. */ verify_all_options(); if( db_get_boolean("dont-push",0) ){ fossil_fatal("pushing is prohibited: the 'dont-push' option is set"); } client_sync(syncFlags, 0, 0); } |
︙ | ︙ | |||
220 221 222 223 224 225 226 227 228 229 230 231 232 233 | ** ** See also: clone, push, pull, remote-url */ void sync_cmd(void){ unsigned configFlags = 0; unsigned syncFlags = SYNC_PUSH|SYNC_PULL; process_sync_args(&configFlags, &syncFlags); if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; client_sync(syncFlags, configFlags, 0); if( (syncFlags & SYNC_PUSH)==0 ){ fossil_warning("pull only: the 'dont-push' option is set"); } } | > > > > | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | ** ** See also: clone, push, pull, remote-url */ void sync_cmd(void){ unsigned configFlags = 0; unsigned syncFlags = SYNC_PUSH|SYNC_PULL; process_sync_args(&configFlags, &syncFlags); /* We should be done with options.. */ verify_all_options(); if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; client_sync(syncFlags, configFlags, 0); if( (syncFlags & SYNC_PUSH)==0 ){ fossil_warning("pull only: the 'dont-push' option is set"); } } |
︙ | ︙ | |||
247 248 249 250 251 252 253 254 255 256 257 258 259 260 | ** See clone usage for possible URL formats. ** ** See also: clone, push, pull, sync */ void remote_url_cmd(void){ char *zUrl; db_find_and_open_repository(0, 0); if( g.argc!=2 && g.argc!=3 ){ usage("remote-url ?URL|off?"); } if( g.argc==3 ){ db_unset("last-sync-url", 0); db_unset("last-sync-pw", 0); if( is_false(g.argv[2]) ) return; | > > > > > | > | | 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | ** See clone usage for possible URL formats. ** ** See also: clone, push, pull, sync */ void remote_url_cmd(void){ char *zUrl; db_find_and_open_repository(0, 0); /* We should be done with options.. */ verify_all_options(); if( g.argc!=2 && g.argc!=3 ){ usage("remote-url ?URL|off?"); } if( g.argc==3 ){ db_unset("last-sync-url", 0); db_unset("last-sync-pw", 0); db_unset("http-auth", 0); if( is_false(g.argv[2]) ) return; url_parse(g.argv[2], URL_REMEMBER|URL_PROMPT_PW|URL_ASK_REMEMBER_PW); } url_remember(); zUrl = db_get("last-sync-url", 0); if( zUrl==0 ){ fossil_print("off\n"); return; }else{ url_parse(zUrl, 0); fossil_print("%s\n", g.url.canonical); } } |
Changes to src/tag.c.
︙ | ︙ | |||
48 49 50 51 52 53 54 | pqueuex_init(&queue); pqueuex_insert(&queue, pid, 0.0, 0); /* Query for children of :pid to which to propagate the tag. ** Three returns: (1) rid of the child. (2) timestamp of child. ** (3) True to propagate or false to block. */ | | | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | pqueuex_init(&queue); pqueuex_insert(&queue, pid, 0.0, 0); /* Query for children of :pid to which to propagate the tag. ** Three returns: (1) rid of the child. (2) timestamp of child. ** (3) True to propagate or false to block. */ db_prepare(&s, "SELECT cid, plink.mtime," " coalesce(srcid=0 AND tagxref.mtime<:mtime, %d) AS doit" " FROM plink LEFT JOIN tagxref ON cid=rid AND tagid=%d" " WHERE pid=:pid AND isprim", tagType==2, tagid ); db_bind_double(&s, ":mtime", mtime); if( tagType==2 ){ /* Set the propagated tag marker on check-in :rid */ db_prepare(&ins, "REPLACE INTO tagxref(tagid, tagtype, srcid, origid, value, mtime, rid)" "VALUES(%d,2,0,%d,%Q,:mtime,:rid)", tagid, origId, zValue ); db_bind_double(&ins, ":mtime", mtime); }else{ /* Remove all references to the tag from check-in :rid */ zValue = 0; db_prepare(&ins, "DELETE FROM tagxref WHERE tagid=%d AND rid=:rid", tagid ); } if( tagid==TAG_BGCOLOR ){ db_prepare(&eventupdate, |
︙ | ︙ | |||
177 178 179 180 181 182 183 | db_bind_double(&s, ":mtime", mtime); rc = db_step(&s); db_finalize(&s); if( rc==SQLITE_ROW ){ /* Another entry that is more recent already exists. Do nothing */ return tagid; } | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | db_bind_double(&s, ":mtime", mtime); rc = db_step(&s); db_finalize(&s); if( rc==SQLITE_ROW ){ /* Another entry that is more recent already exists. Do nothing */ return tagid; } db_prepare(&s, "REPLACE INTO tagxref(tagid,tagtype,srcId,origid,value,mtime,rid)" " VALUES(%d,%d,%d,%d,%Q,:mtime,%d)", tagid, tagtype, srcId, rid, zValue, rid ); db_bind_double(&s, ":mtime", mtime); db_step(&s); db_finalize(&s); |
︙ | ︙ | |||
211 212 213 214 215 216 217 | db_multi_exec( "INSERT OR IGNORE INTO private(rid) VALUES(%d);", rid ); } } if( zCol ){ | | > | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | db_multi_exec( "INSERT OR IGNORE INTO private(rid) VALUES(%d);", rid ); } } if( zCol ){ db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d", zCol, zValue, rid); if( tagid==TAG_COMMENT ){ char *zCopy = mprintf("%s", zValue); wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE); free(zCopy); } } if( tagid==TAG_DATE ){ |
︙ | ︙ | |||
254 255 256 257 258 259 260 | usage("TAGNAME ARTIFACT-ID ?VALUE?"); } zTag = g.argv[2]; switch( zTag[0] ){ case '+': tagtype = 1; break; case '*': tagtype = 2; break; case '-': tagtype = 0; break; | | | | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | usage("TAGNAME ARTIFACT-ID ?VALUE?"); } zTag = g.argv[2]; switch( zTag[0] ){ case '+': tagtype = 1; break; case '*': tagtype = 2; break; case '-': tagtype = 0; break; default: fossil_fatal("tag should begin with '+', '*', or '-'"); return; } rid = name_to_rid(g.argv[3]); if( rid==0 ){ fossil_fatal("no such object: %s", g.argv[3]); } g.markPrivate = content_is_private(rid); zValue = g.argc==5 ? g.argv[4] : 0; db_begin_transaction(); tag_insert(zTag, tagtype, zValue, -1, 0.0, rid); db_end_transaction(0); } /* ** Add a control record to the repository that either creates ** or cancels a tag. */ void tag_add_artifact( |
︙ | ︙ | |||
320 321 322 323 324 325 326 | blob_appendf(&ctrl, "T %c%s%F %b", zTagtype[tagtype], zPrefix, zTagname, &uuid); if( tagtype>0 && zValue && zValue[0] ){ blob_appendf(&ctrl, " %F\n", zValue); }else{ blob_appendf(&ctrl, "\n"); } | | | | 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | blob_appendf(&ctrl, "T %c%s%F %b", zTagtype[tagtype], zPrefix, zTagname, &uuid); if( tagtype>0 && zValue && zValue[0] ){ blob_appendf(&ctrl, " %F\n", zValue); }else{ blob_appendf(&ctrl, "\n"); } blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : login_name()); md5sum_blob(&ctrl, &cksum); blob_appendf(&ctrl, "Z %b\n", &cksum); nrid = content_put(&ctrl); manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); assert( blob_is_reset(&ctrl) ); } /* ** COMMAND: tag ** Usage: %fossil tag SUBCOMMAND ... ** |
︙ | ︙ | |||
349 350 351 352 353 354 355 | ** ** Remove the tag TAGNAME from CHECK-IN, and also remove ** the propagation of the tag to any descendants. ** ** %fossil tag find ?--raw? ?-t|--type TYPE? ?-n|--limit #? TAGNAME ** ** List all objects that use TAGNAME. TYPE can be "ci" for | | | | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | ** ** Remove the tag TAGNAME from CHECK-IN, and also remove ** the propagation of the tag to any descendants. ** ** %fossil tag find ?--raw? ?-t|--type TYPE? ?-n|--limit #? TAGNAME ** ** List all objects that use TAGNAME. TYPE can be "ci" for ** check-ins or "e" for events. The limit option limits the number ** of results to the given value. ** ** %fossil tag list|ls ?--raw? ?CHECK-IN? ** ** List all tags, or if CHECK-IN is supplied, list ** all tags and their values for CHECK-IN. ** ** The option --raw allows the manipulation of all types of tags ** used for various internal purposes in fossil. It also shows ** "cancel" tags for the "find" and "list" subcommands. You should |
︙ | ︙ | |||
376 377 378 379 380 381 382 | ** will be taken as an artifact or baseline ID and fossil will ** probably complain that no such revision was found. However ** ** fossil update tag:decaf ** ** will assume that "decaf" is a tag/branch name. ** | | | | | | 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | ** will be taken as an artifact or baseline ID and fossil will ** probably complain that no such revision was found. However ** ** fossil update tag:decaf ** ** will assume that "decaf" is a tag/branch name. ** ** only allow --date-override and --user-override in ** %fossil tag add --date-override 'YYYY-MMM-DD HH:MM:SS' \\ ** --user-override user ** in order to import history from other scm systems */ void tag_cmd(void){ int n; int fRaw = find_option("raw","",0)!=0; int fPropagate = find_option("propagate","",0)!=0; const char *zPrefix = fRaw ? "" : "sym-"; const char *zFindLimit = find_option("limit","n",1); const int nFindLimit = zFindLimit ? atoi(zFindLimit) : -2000; db_find_and_open_repository(0, 0); if( g.argc<3 ){ goto tag_cmd_usage; } n = strlen(g.argv[2]); if( n==0 ){ |
︙ | ︙ | |||
435 436 437 438 439 440 441 | const char *zType = find_option("type","t",1); Blob sql = empty_blob; if( zType==0 || zType[0]==0 ) zType = "*"; if( g.argc!=4 ){ usage("find ?--raw? ?-t|--type TYPE? ?-n|--limit #? TAGNAME"); } if( fRaw ){ | | | | | | < < < | | | | | 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 | const char *zType = find_option("type","t",1); Blob sql = empty_blob; if( zType==0 || zType[0]==0 ) zType = "*"; if( g.argc!=4 ){ usage("find ?--raw? ?-t|--type TYPE? ?-n|--limit #? TAGNAME"); } if( fRaw ){ blob_append_sql(&sql, "SELECT blob.uuid FROM tagxref, blob" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" " AND tagxref.tagtype>0" " AND blob.rid=tagxref.rid", g.argv[3] ); if( nFindLimit>0 ){ blob_append_sql(&sql, " LIMIT %d", nFindLimit); } db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%s\n", db_column_text(&q, 0)); } db_finalize(&q); }else{ int tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", g.argv[3]); if( tagid>0 ){ blob_append_sql(&sql, "%s" " AND event.type GLOB '%q'" " AND blob.rid IN (" " SELECT rid FROM tagxref" " WHERE tagtype>0 AND tagid=%d" ")" " ORDER BY event.mtime DESC", timeline_query_for_tty(), zType, tagid ); db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); print_timeline(&q, nFindLimit, 79, 0); db_finalize(&q); } } }else if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){ Stmt q; if( g.argc==3 ){ db_prepare(&q, "SELECT tagname FROM tag" " WHERE EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=tag.tagid" " AND tagtype>0)" " ORDER BY tagname" ); while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
535 536 537 538 539 540 541 | return; tag_cmd_usage: usage("add|cancel|find|list ..."); } /* | | | > | | | < < < < < < | 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 | return; tag_cmd_usage: usage("add|cancel|find|list ..."); } /* ** WEBPAGE: taglist */ void taglist_page(void){ Stmt q; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); } login_anonymous_available(); style_header("Tags"); style_adunit_config(ADUNIT_RIGHT_OK); style_submenu_element("Timeline", "Timeline", "tagtimeline"); @ <h2>Non-propagating tags:</h2> db_prepare(&q, "SELECT substr(tagname,5)" " FROM tag" " WHERE EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=tag.tagid" " AND tagtype=1)" " AND tagname GLOB 'sym-*'" " ORDER BY tagname" ); @ <ul> while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); if( g.perm.Hyperlink ){ @ <li>%z(xhref("class='taglink'","%R/timeline?t=%T&n=200",zName)) @ %h(zName)</a></li> }else{ @ <li><span class="tagDsp">%h(zName)</span></li> } } @ </ul> db_finalize(&q); style_footer(); } /* ** WEBPAGE: /tagtimeline */ void tagtimeline_page(void){ Stmt q; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("Tagged Check-ins"); style_submenu_element("List", "List", "taglist"); login_anonymous_available(); @ <h2>Check-ins with non-propagating tags:</h2> db_prepare(&q, "%s AND blob.rid IN (SELECT rid FROM tagxref" " WHERE tagtype=1 AND srcid>0" " AND tagid IN (SELECT tagid FROM tag " " WHERE tagname GLOB 'sym-*'))" " ORDER BY event.mtime DESC", timeline_query_for_www() ); www_print_timeline(&q, 0, 0, 0, 0, 0); db_finalize(&q); @ <br /> style_footer(); } |
Changes to src/tar.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to generate tarballs. */ #include <assert.h> | > > > > > | < > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to generate tarballs. */ #include "config.h" #include <assert.h> #if defined(FOSSIL_ENABLE_MINIZ) # define MINIZ_HEADER_FILE_ONLY # include "miniz.c" #else # include <zlib.h> #endif #include "tar.h" /* ** State information for the tarball builder. */ static struct tarball_t { unsigned char *aHdr; /* Space for building headers */ |
︙ | ︙ | |||
278 279 280 281 282 283 284 | */ static void tar_add_header( const char *zName, /* Name of the object */ int nName, /* Number of characters in zName */ int iMode, /* Mode. 0644 or 0755 */ unsigned int mTime, /* File modification time */ int iSize, /* Size of the object in bytes */ | | | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 | */ static void tar_add_header( const char *zName, /* Name of the object */ int nName, /* Number of characters in zName */ int iMode, /* Mode. 0644 or 0755 */ unsigned int mTime, /* File modification time */ int iSize, /* Size of the object in bytes */ char cType /* Type of object: '0'==file. '2'==symlink. '5'==directory */ ){ /* set mode and modification time */ sqlite3_snprintf(8, (char*)&tball.aHdr[100], "%07o", iMode); sqlite3_snprintf(12, (char*)&tball.aHdr[136], "%011o", mTime); /* see if we need to output a Pax Interchange Header */ |
︙ | ︙ | |||
334 335 336 337 338 339 340 | const char *zName, /* Name of directory including final "/" */ int nName, /* Characters in zName */ unsigned int mTime /* Modification time */ ){ int i; for(i=nName-1; i>0 && zName[i]!='/'; i--){} if( i<=0 ) return; | | | > | 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | const char *zName, /* Name of directory including final "/" */ int nName, /* Characters in zName */ unsigned int mTime /* Modification time */ ){ int i; for(i=nName-1; i>0 && zName[i]!='/'; i--){} if( i<=0 ) return; if( i<tball.nPrevDirAlloc && strncmp(tball.zPrevDir, zName, i)==0 && tball.zPrevDir[i]==0 ) return; db_multi_exec("INSERT OR IGNORE INTO dir VALUES('%#q')", i, zName); if( sqlite3_changes(g.db)==0 ) return; tar_add_directory_of(zName, i-1, mTime); tar_add_header(zName, i, 0755, mTime, 0, '5'); if( i >= tball.nPrevDirAlloc ){ int nsize = tball.nPrevDirAlloc * 2; if(i+1 > nsize) |
︙ | ︙ | |||
369 370 371 372 373 374 375 | int n = blob_size(pContent); int lastPage; char cType = '0'; /* length check moved to tar_split_path */ tar_add_directory_of(zName, nName, mTime); | | | | 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | int n = blob_size(pContent); int lastPage; char cType = '0'; /* length check moved to tar_split_path */ tar_add_directory_of(zName, nName, mTime); /* * If we have a symlink, write its destination path (which is stored in * pContent) into header, and set content length to 0 to avoid storing path * as file content in the next step. Since 'linkname' header is limited to * 100 bytes (-1 byte for terminating zero), if path is greater than that, * store symlink as a plain-text file. (Not sure how TAR handles long links.) */ if( mPerm == PERM_LNK && n <= 100 ){ sqlite3_snprintf(100, (char*)&tball.aHdr[157], "%s", blob_str(pContent)); cType = '2'; n = 0; } tar_add_header(zName, nName, ( mPerm==PERM_EXE ) ? 0755 : 0644, mTime, n, cType); if( n ){ gzip_step(blob_buffer(pContent), n); lastPage = n % 512; if( lastPage!=0 ){ gzip_step(tball.zSpaces, 512 - lastPage); } |
︙ | ︙ | |||
420 421 422 423 424 425 426 | ** ** Generate a GZIP-compressed tarball in the file given by the first argument ** that contains files given in the second and subsequent arguments. */ void test_tarball_cmd(void){ int i; Blob zip; | < | > | < | | | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | ** ** Generate a GZIP-compressed tarball in the file given by the first argument ** that contains files given in the second and subsequent arguments. */ void test_tarball_cmd(void){ int i; Blob zip; if( g.argc<3 ){ usage("ARCHIVE FILE...."); } sqlite3_open(":memory:", &g.db); tar_begin(-1); for(i=3; i<g.argc; i++){ Blob file; blob_zero(&file); blob_read_from_file(&file, g.argv[i]); tar_add_file(g.argv[i], &file, file_wd_perm(0), file_wd_mtime(0)); blob_reset(&file); } tar_finish(&zip); blob_write_to_file(&zip, g.argv[2]); } /* ** Given the RID for a check-in, construct a tarball containing ** all files in that check-in ** ** If RID is for an object that is not a real manifest, then the ** resulting tarball contains a single file which is the RID ** object. ** ** If the RID object does not exist in the repository, then ** pTar is zeroed. |
︙ | ︙ | |||
484 485 486 487 488 489 490 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); if( pManifest ){ mTime = (pManifest->rDate - 2440587.5)*86400.0; tar_begin(mTime); if( db_get_boolean("manifest", 0) ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); | < > > | 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); if( pManifest ){ mTime = (pManifest->rDate - 2440587.5)*86400.0; tar_begin(mTime); if( db_get_boolean("manifest", 0) ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); sha1sum_blob(&mfile, &hash); sterilize_manifest(&mfile); tar_add_file(zName, &mfile, 0, mTime); blob_reset(&mfile); blob_append(&hash, "\n", 1); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.uuid", -1); zName = blob_str(&filename); tar_add_file(zName, &hash, 0, mTime); blob_reset(&hash); |
︙ | ︙ | |||
523 524 525 526 527 528 529 | blob_reset(&filename); tar_finish(pTar); } /* ** COMMAND: tarball* ** | | > > > > > > > > | | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 | blob_reset(&filename); tar_finish(pTar); } /* ** COMMAND: tarball* ** ** Usage: %fossil tarball VERSION OUTPUTFILE ** ** Generate a compressed tarball for a specified version. If the --name ** option is used, its argument becomes the name of the top-level directory ** in the resulting tarball. If --name is omitted, the top-level directory ** named is derived from the project name, the check-in date and time, and ** the artifact ID of the check-in. ** ** Options: ** --name DIRECTORYNAME The name of the top-level directory in the archive ** -R REPOSITORY Specify a Fossil repository */ void tarball_cmd(void){ int rid; Blob tarball; const char *zName; zName = find_option("name", 0, 1); db_find_and_open_repository(0, 0); /* We should be done with options.. */ verify_all_options(); if( g.argc!=4 ){ usage("VERSION OUTPUTFILE"); } rid = name_to_typed_rid(g.argv[2], "ci"); if( rid==0 ){ fossil_fatal("Check-in not found: %s", g.argv[2]); return; } if( zName==0 ){ zName = db_text("default-name", "SELECT replace(%Q,' ','_') " " || strftime('_%%Y-%%m-%%d_%%H%%M%%S_', event.mtime) " |
︙ | ︙ | |||
566 567 568 569 570 571 572 | blob_reset(&tarball); } /* ** WEBPAGE: tarball ** URL: /tarball/RID.tar.gz ** | | > > > > > > > > > > > | | > | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 | blob_reset(&tarball); } /* ** WEBPAGE: tarball ** URL: /tarball/RID.tar.gz ** ** Generate a compressed tarball for a check-in. ** Return that tarball as the HTTP reply content. ** ** Optional URL Parameters: ** ** - name=NAME[.tar.gz] is base name of the output file. Defaults to ** something project/version-specific. The prefix of the name, up to ** the last '.', are used as the top-most directory name in the tar ** output. ** ** - uuid=the version to tar (may be a tag/branch name). ** Defaults to "trunk". ** */ void tarball_page(void){ int rid; char *zName, *zRid, *zKey; int nName, nRid; Blob tarball; login_check_credentials(); if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; } load_control(); zName = mprintf("%s", PD("name","")); nName = strlen(zName); zRid = mprintf("%s", PD("uuid","trunk")); nRid = strlen(zRid); if( nName>7 && fossil_strcmp(&zName[nName-7], ".tar.gz")==0 ){ /* Special case: Remove the ".tar.gz" suffix. */ nName -= 7; |
︙ | ︙ | |||
601 602 603 604 605 606 607 | } rid = name_to_typed_rid(nRid?zRid:zName, "ci"); if( rid==0 ){ @ Not found return; } if( nRid==0 && nName>10 ) zName[10] = 0; | > > > > > > > > > > > > > > > > > > > > > > > | > > > | 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | } rid = name_to_typed_rid(nRid?zRid:zName, "ci"); if( rid==0 ){ @ Not found return; } if( nRid==0 && nName>10 ) zName[10] = 0; zKey = db_text(0, "SELECT '/tarball/'||uuid||'/%q'" " FROM blob WHERE rid=%d",zName,rid); if( P("debug")!=0 ){ style_header("Tarball Generator Debug Screen"); @ zName = "%h(zName)"<br> @ rid = %d(rid)<br> @ zKey = "%h(zKey)" style_footer(); return; } if( referred_from_login() ){ style_header("Tarball Download"); @ <form action='%R/tarball'> cgi_query_parameters_to_hidden(); @ <p>Tarball named <b>%h(zName).tar.gz</b> holding the content @ of check-in <b>%h(zRid)</b>: @ <input type="submit" value="Download" /> @ </form> style_footer(); return; } blob_zero(&tarball); if( cache_read(&tarball, zKey)==0 ){ tarball_of_checkin(rid, &tarball, zName); cache_write(&tarball, zKey); } free( zName ); free( zRid ); free( zKey ); cgi_set_content(&tarball); cgi_set_content_type("application/x-compressed"); } |
Changes to src/th.c.
1 2 | /* | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /* ** The implementation of the TH core. This file contains the parser, and ** the implementation of the interface in th.h. */ #include "config.h" #include "th.h" #include <string.h> #include <assert.h> typedef struct Th_Command Th_Command; typedef struct Th_Frame Th_Frame; typedef struct Th_Variable Th_Variable; /* ** Interpreter structure. */ struct Th_Interp { Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */ char *zResult; /* Current interpreter result (Th_Malloc()ed) */ int nResult; /* number of bytes in zResult */ Th_Hash *paCmd; /* Table of registered commands */ Th_Frame *pFrame; /* Current execution frame */ int isListMode; /* True if thSplitList() should operate in "list" mode */ }; /* |
︙ | ︙ | |||
39 40 41 42 43 44 45 | ** Each stack frame (variable scope) is represented by an instance ** of this structure. Variable values set using the Th_SetVar command ** are stored in the Th_Frame.paVar hash table member of the associated ** stack frame object. ** ** When an interpreter is created, a single Th_Frame structure is also ** allocated - the global variable scope. Th_Interp.pFrame (the current | | | | | | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | ** Each stack frame (variable scope) is represented by an instance ** of this structure. Variable values set using the Th_SetVar command ** are stored in the Th_Frame.paVar hash table member of the associated ** stack frame object. ** ** When an interpreter is created, a single Th_Frame structure is also ** allocated - the global variable scope. Th_Interp.pFrame (the current ** interpreter frame) is initialised to point to this Th_Frame. It is ** not deleted for the lifetime of the interpreter (because the global ** frame never goes out of scope). ** ** New stack frames are created by the Th_InFrame() function. Before ** invoking its callback function, Th_InFrame() allocates a new Th_Frame ** structure with pCaller set to the current frame (Th_Interp.pFrame), ** and sets the current frame to the new frame object. After the callback ** has been invoked, the allocated Th_Frame is deleted and the value ** of the current frame pointer restored. ** ** By default, the Th_SetVar(), Th_UnsetVar() and Th_GetVar() functions ** access variable values in the current frame. If they need to access ** the global frame, they do so by traversing the pCaller pointer list. ** Likewise, the Th_LinkVar() function uses the pCaller pointers to ** link to variables located in the global or other stack frames. */ struct Th_Frame { Th_Hash *paVar; /* Variables defined in this scope */ Th_Frame *pCaller; /* Calling frame */ }; |
︙ | ︙ | |||
81 82 83 84 85 86 87 | ** a hash table mapping between array key name (a th1 string) and ** a pointer to the Th_Variable structure holding the scalar ** value. */ struct Th_Variable { int nRef; /* Number of references to this structure */ int nData; /* Number of bytes at Th_Variable.zData */ | | | | | | > | | | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** a hash table mapping between array key name (a th1 string) and ** a pointer to the Th_Variable structure holding the scalar ** value. */ struct Th_Variable { int nRef; /* Number of references to this structure */ int nData; /* Number of bytes at Th_Variable.zData */ char *zData; /* Data for scalar variables */ Th_Hash *pHash; /* Data for array variables */ }; /* ** Hash table API: */ #define TH_HASHSIZE 257 struct Th_Hash { Th_HashEntry *a[TH_HASHSIZE]; }; static int thEvalLocal(Th_Interp *, const char *, int); static int thSplitList(Th_Interp*, const char*, int, char***, int **, int*); static int thHexdigit(char c); static int thEndOfLine(const char *, int); static int thPushFrame(Th_Interp*, Th_Frame*); static void thPopFrame(Th_Interp*); static int thFreeVariable(Th_HashEntry*, void*); static int thFreeCommand(Th_HashEntry*, void*); /* ** The following are used by both the expression and language parsers. ** Given that the start of the input string (z, n) is a language ** construct of the relevant type (a command enclosed in [], an escape ** sequence etc.), these functions determine the number of bytes ** of the input consumed by the construct. For example: ** ** int nByte; ** thNextCommand(interp, "[expr $a+1] $nIter", 18, &nByte); ** ** results in variable nByte being set to 11. Or, ** ** thNextVarname(interp, "$a+1", 4, &nByte); ** ** results in nByte being set to 2. */ static int thNextCommand(Th_Interp*, const char *z, int n, int *pN); static int thNextEscape (Th_Interp*, const char *z, int n, int *pN); static int thNextVarname(Th_Interp*, const char *z, int n, int *pN); static int thNextNumber (Th_Interp*, const char *z, int n, int *pN); static int thNextInteger (Th_Interp*, const char *z, int n, int *pN); static int thNextSpace (Th_Interp*, const char *z, int n, int *pN); /* ** Given that the input string (z, n) contains a language construct of ** the relevant type (a command enclosed in [], an escape sequence ** like "\xFF" or a variable reference like "${varname}", perform ** substitution on the string and store the resulting string in ** the interpreter result. */ static int thSubstCommand(Th_Interp*, const char *z, int n); static int thSubstEscape (Th_Interp*, const char *z, int n); static int thSubstVarname(Th_Interp*, const char *z, int n); /* ** Given that there is a th1 word located at the start of the input ** string (z, n), determine the length in bytes of that word. If the ** isCmd argument is non-zero, then an unescaped ";" byte not ** located inside of a block or quoted string is considered to mark ** the end of the word. */ static int thNextWord(Th_Interp*, const char *z, int n, int *pN, int isCmd); /* ** Perform substitution on the word contained in the input string (z, n). ** Store the resulting string in the interpreter result. |
︙ | ︙ | |||
173 174 175 176 177 178 179 | /* ** Append nAdd bytes of content copied from zAdd to the end of buffer ** pBuffer. If there is not enough space currently allocated, resize ** the allocation to make space. */ static int thBufferWrite( | | | | | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | /* ** Append nAdd bytes of content copied from zAdd to the end of buffer ** pBuffer. If there is not enough space currently allocated, resize ** the allocation to make space. */ static int thBufferWrite( Th_Interp *interp, Buffer *pBuffer, const char *zAdd, int nAdd ){ int nReq; if( nAdd<0 ){ nAdd = th_strlen(zAdd); } |
︙ | ︙ | |||
255 256 257 258 259 260 261 262 | /* ** Argument pEntry points to an entry in a stack frame hash table ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable ** structure that the entry points to. Free the Th_Variable if its ** reference count reaches 0. ** ** Argument pContext is a pointer to the interpreter structure. */ | > > | > > > > > | > | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | /* ** Argument pEntry points to an entry in a stack frame hash table ** (Th_Frame.paVar). Decrement the reference count of the Th_Variable ** structure that the entry points to. Free the Th_Variable if its ** reference count reaches 0. ** ** Argument pContext is a pointer to the interpreter structure. ** ** Returns non-zero if the Th_Variable was actually freed. */ static int thFreeVariable(Th_HashEntry *pEntry, void *pContext){ Th_Variable *pValue = (Th_Variable *)pEntry->pData; pValue->nRef--; assert( pValue->nRef>=0 ); if( pValue->nRef==0 ){ Th_Interp *interp = (Th_Interp *)pContext; Th_Free(interp, pValue->zData); if( pValue->pHash ){ Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); Th_HashDelete(interp, pValue->pHash); } Th_Free(interp, pValue); pEntry->pData = 0; return 1; } return 0; } /* ** Argument pEntry points to an entry in the command hash table ** (Th_Interp.paCmd). Delete the Th_Command structure that the ** entry points to. ** ** Argument pContext is a pointer to the interpreter structure. ** ** Always returns non-zero. */ static int thFreeCommand(Th_HashEntry *pEntry, void *pContext){ Th_Command *pCommand = (Th_Command *)pEntry->pData; if( pCommand->xDel ){ pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); } Th_Free((Th_Interp *)pContext, pEntry->pData); pEntry->pData = 0; return 1; } /* ** Push a new frame onto the stack. */ static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){ pFrame->paVar = Th_HashNew(interp); |
︙ | ︙ | |||
308 309 310 311 312 313 314 | Th_Frame *pFrame = interp->pFrame; Th_HashIterate(interp, pFrame->paVar, thFreeVariable, (void *)interp); Th_HashDelete(interp, pFrame->paVar); interp->pFrame = pFrame->pCaller; } /* | | | | | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | Th_Frame *pFrame = interp->pFrame; Th_HashIterate(interp, pFrame->paVar, thFreeVariable, (void *)interp); Th_HashDelete(interp, pFrame->paVar); interp->pFrame = pFrame->pCaller; } /* ** The first part of the string (zInput,nInput) contains an escape ** sequence. Set *pnEscape to the number of bytes in the escape sequence. ** If there is a parse error, return TH_ERROR and set the interpreter ** result to an error message. Otherwise return TH_OK. */ static int thNextEscape( Th_Interp *interp, const char *zInput, int nInput, int *pnEscape ){ int i = 2; assert(nInput>0); assert(zInput[0]=='\\'); |
︙ | ︙ | |||
341 342 343 344 345 346 347 | } *pnEscape = i; return TH_OK; } /* ** The first part of the string (zInput,nInput) contains a variable | | | | | | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | } *pnEscape = i; return TH_OK; } /* ** The first part of the string (zInput,nInput) contains a variable ** reference. Set *pnVarname to the number of bytes in the variable ** reference. If there is a parse error, return TH_ERROR and set the ** interpreter result to an error message. Otherwise return TH_OK. */ int thNextVarname( Th_Interp *interp, const char *zInput, int nInput, int *pnVarname ){ int i; assert(nInput>0); assert(zInput[0]=='$'); |
︙ | ︙ | |||
398 399 400 401 402 403 404 | *pnVarname = i; return TH_OK; } /* ** The first part of the string (zInput,nInput) contains a command | | | | | | | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | *pnVarname = i; return TH_OK; } /* ** The first part of the string (zInput,nInput) contains a command ** enclosed in a "[]" block. Set *pnCommand to the number of bytes in ** the variable reference. If there is a parse error, return TH_ERROR ** and set the interpreter result to an error message. Otherwise return ** TH_OK. */ int thNextCommand( Th_Interp *interp, const char *zInput, int nInput, int *pnCommand ){ int nBrace = 0; int nSquare = 0; int i; assert(nInput>0); |
︙ | ︙ | |||
435 436 437 438 439 440 441 | *pnCommand = i; return TH_OK; } /* | | | | | | | | | | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 | *pnCommand = i; return TH_OK; } /* ** Set *pnSpace to the number of whitespace bytes at the start of ** input string (zInput, nInput). Always return TH_OK. */ int thNextSpace( Th_Interp *interp, const char *zInput, int nInput, int *pnSpace ){ int i; for(i=0; i<nInput && th_isspace(zInput[i]); i++); *pnSpace = i; return TH_OK; } /* ** The first byte of the string (zInput,nInput) is not white-space. ** Set *pnWord to the number of bytes in the th1 word that starts ** with this byte. If a complete word cannot be parsed or some other ** error occurs, return TH_ERROR and set the interpreter result to ** an error message. Otherwise return TH_OK. ** ** If the isCmd argument is non-zero, then an unescaped ";" byte not ** located inside of a block or quoted string is considered to mark ** the end of the word. */ static int thNextWord( Th_Interp *interp, const char *zInput, int nInput, int *pnWord, int isCmd ){ int iEnd = 0; assert( !th_isspace(zInput[0]) ); |
︙ | ︙ | |||
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 | case '[': if( nBrace==0 ) nSq++; break; case ']': if( nBrace==0 ) nSq--; break; } iEnd++; } if( nBrace>0 || nSq>0 ){ /* Parse error */ return TH_ERROR; } } if( iEnd>nInput ){ /* Parse error */ return TH_ERROR; } *pnWord = iEnd; return TH_OK; } /* | > > | 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | case '[': if( nBrace==0 ) nSq++; break; case ']': if( nBrace==0 ) nSq--; break; } iEnd++; } if( nBrace>0 || nSq>0 ){ /* Parse error */ Th_SetResult(interp, "parse error", -1); return TH_ERROR; } } if( iEnd>nInput ){ /* Parse error */ Th_SetResult(interp, "parse error", -1); return TH_ERROR; } *pnWord = iEnd; return TH_OK; } /* |
︙ | ︙ | |||
528 529 530 531 532 533 534 | assert(nWord>=2); assert(zWord[0]=='[' && zWord[nWord-1]==']'); return thEvalLocal(interp, &zWord[1], nWord-2); } /* ** The input string (zWord, nWord) contains a th1 variable reference | | | | 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 | assert(nWord>=2); assert(zWord[0]=='[' && zWord[nWord-1]==']'); return thEvalLocal(interp, &zWord[1], nWord-2); } /* ** The input string (zWord, nWord) contains a th1 variable reference ** (a '$' byte followed by a variable name). Perform substitution on ** the input string and store the resulting string in the interpreter ** result. */ static int thSubstVarname( Th_Interp *interp, const char *zWord, int nWord ){ |
︙ | ︙ | |||
569 570 571 572 573 574 575 | } } return Th_GetVar(interp, &zWord[1], nWord-1); } /* ** The input string (zWord, nWord) contains a th1 escape sequence. | | | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | } } return Th_GetVar(interp, &zWord[1], nWord-1); } /* ** The input string (zWord, nWord) contains a th1 escape sequence. ** Perform substitution on the input string and store the resulting ** string in the interpreter result. */ static int thSubstEscape( Th_Interp *interp, const char *zWord, int nWord ){ |
︙ | ︙ | |||
605 606 607 608 609 610 611 | Th_SetResult(interp, &c, 1); return TH_OK; } /* ** The input string (zWord, nWord) contains a th1 word. Perform | | | 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 | Th_SetResult(interp, &c, 1); return TH_OK; } /* ** The input string (zWord, nWord) contains a th1 word. Perform ** substitution on the input string and store the resulting ** string in the interpreter result. */ static int thSubstWord( Th_Interp *interp, const char *zWord, int nWord ){ |
︙ | ︙ | |||
637 638 639 640 641 642 643 | int nGet; int (*xGet)(Th_Interp *, const char*, int, int *) = 0; int (*xSubst)(Th_Interp *, const char*, int) = 0; switch( zWord[i] ){ case '\\': | | | | | 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 | int nGet; int (*xGet)(Th_Interp *, const char*, int, int *) = 0; int (*xSubst)(Th_Interp *, const char*, int) = 0; switch( zWord[i] ){ case '\\': xGet = thNextEscape; xSubst = thSubstEscape; break; case '[': if( !interp->isListMode ){ xGet = thNextCommand; xSubst = thSubstCommand; break; } case '$': if( !interp->isListMode ){ xGet = thNextVarname; xSubst = thSubstVarname; break; } default: { thBufferWrite(interp, &output, &zWord[i], 1); continue; /* Go to the next iteration of the for(...) loop */ } } |
︙ | ︙ | |||
682 683 684 685 686 687 688 | /* ** Return true if one of the following is true of the buffer pointed ** to by zInput, length nInput: ** ** + It is empty, or ** + It contains nothing but white-space, or | | | 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 | /* ** Return true if one of the following is true of the buffer pointed ** to by zInput, length nInput: ** ** + It is empty, or ** + It contains nothing but white-space, or ** + It contains no non-white-space characters before the first ** newline character. ** ** Otherwise return false. */ static int thEndOfLine(const char *zInput, int nInput){ int i; for(i=0; i<nInput && zInput[i]!='\n' && th_isspace(zInput[i]); i++); |
︙ | ︙ | |||
722 723 724 725 726 727 728 | ** Th_SplitList(interp, zList, nList, &argv, &argl, &argc); ** ** // Free all memory allocated by Th_SplitList(). The arrays pointed ** // to by argv and argl are invalidated by this call. ** // ** Th_Free(interp, argv); ** | | | | | 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 | ** Th_SplitList(interp, zList, nList, &argv, &argl, &argc); ** ** // Free all memory allocated by Th_SplitList(). The arrays pointed ** // to by argv and argl are invalidated by this call. ** // ** Th_Free(interp, argv); ** */ static int thSplitList( Th_Interp *interp, /* Interpreter context */ const char *zList, /* Pointer to buffer containing input list */ int nList, /* Size of buffer pointed to by zList */ char ***pazElem, /* OUT: Array of list elements */ int **panElem, /* OUT: Lengths of each list element */ int *pnCount /* OUT: Number of list elements */ ){ int rc = TH_OK; Buffer strbuf; Buffer lenbuf; |
︙ | ︙ | |||
771 772 773 774 775 776 777 | } } assert((lenbuf.nBuf/sizeof(int))==nCount); assert((pazElem && panElem) || (!pazElem && !panElem)); if( pazElem && rc==TH_OK ){ int i; | | | | | 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 | } } assert((lenbuf.nBuf/sizeof(int))==nCount); assert((pazElem && panElem) || (!pazElem && !panElem)); if( pazElem && rc==TH_OK ){ int i; char *zElem; int *anElem; char **azElem = Th_Malloc(interp, sizeof(char*) * nCount + /* azElem */ sizeof(int) * nCount + /* anElem */ strbuf.nBuf /* space for list element strings */ ); anElem = (int *)&azElem[nCount]; zElem = (char *)&anElem[nCount]; memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf); memcpy(zElem, strbuf.zBuf, strbuf.nBuf); for(i=0; i<nCount;i++){ azElem[i] = zElem; zElem += (anElem[i] + 1); } *pazElem = azElem; *panElem = anElem; } if( pnCount ){ *pnCount = nCount; } finish: thBufferFree(interp, &strbuf); thBufferFree(interp, &lenbuf); return rc; } /* |
︙ | ︙ | |||
873 874 875 876 877 878 879 | /* Call the command procedure. */ if( rc==TH_OK ){ Th_Command *p = (Th_Command *)(pEntry->pData); const char **azArg = (const char **)argv; rc = p->xProc(interp, p->pContext, argc, azArg, argl); } | | | | 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 | /* Call the command procedure. */ if( rc==TH_OK ){ Th_Command *p = (Th_Command *)(pEntry->pData); const char **azArg = (const char **)argv; rc = p->xProc(interp, p->pContext, argc, azArg, argl); } /* If an error occurred, add this command to the stack trace report. */ if( rc==TH_ERROR ){ char *zRes; int nRes; char *zStack = 0; int nStack = 0; zRes = Th_TakeResult(interp, &nRes); if( TH_OK==Th_GetVar(interp, (char *)"::th_stack_trace", -1) ){ zStack = Th_TakeResult(interp, &nStack); } Th_ListAppend(interp, &zStack, &nStack, zFirst, zInput-zFirst); Th_SetVar(interp, (char *)"::th_stack_trace", -1, zStack, nStack); Th_SetResult(interp, zRes, nRes); |
︙ | ︙ | |||
909 910 911 912 913 914 915 | ** Th_Frame structure. If unsuccessful (no such frame), return 0 and ** leave an error message in the interpreter result. ** ** Argument iFrame is interpreted as follows: ** ** * If iFrame is 0, this means the current frame. ** | | | | | 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 | ** Th_Frame structure. If unsuccessful (no such frame), return 0 and ** leave an error message in the interpreter result. ** ** Argument iFrame is interpreted as follows: ** ** * If iFrame is 0, this means the current frame. ** ** * If iFrame is negative, then the nth frame up the stack, where ** n is the absolute value of iFrame. A value of -1 means the ** calling procedure. ** ** * If iFrame is +ve, then the nth frame from the bottom of the ** stack. An iFrame value of 1 means the toplevel (global) frame. */ static Th_Frame *getFrame(Th_Interp *interp, int iFrame){ Th_Frame *p = interp->pFrame; int i; if( iFrame>0 ){ for(i=0; p; i++){ |
︙ | ︙ | |||
945 946 947 948 949 950 951 | return p; } /* ** Evaluate th1 script (zProgram, nProgram) in the frame identified by ** argument iFrame. Leave either an error message or a result in the | | | | | 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 | return p; } /* ** Evaluate th1 script (zProgram, nProgram) in the frame identified by ** argument iFrame. Leave either an error message or a result in the ** interpreter result and return a th1 error code (TH_OK, TH_ERROR, ** TH_RETURN, TH_CONTINUE or TH_BREAK). */ int Th_Eval(Th_Interp *interp, int iFrame, const char *zProgram, int nProgram){ int rc = TH_OK; Th_Frame *pSavedFrame = interp->pFrame; /* Set Th_Interp.pFrame to the frame that this script is to be ** evaluated in. The current frame is saved in pSavedFrame and will ** be restored before this function returns. */ interp->pFrame = getFrame(interp, iFrame); if( !interp->pFrame ){ rc = TH_ERROR; }else{ int nInput = nProgram; if( nInput<0 ){ nInput = th_strlen(zProgram); } rc = thEvalLocal(interp, zProgram, nInput); } interp->pFrame = pSavedFrame; |
︙ | ︙ | |||
992 993 994 995 996 997 998 | ** array variable. If the variable is a scalar, *pzInner is set to 0. ** If it is an array variable, (*pzInner, *pnInner) is set to the ** array key name. */ static int thAnalyseVarname( const char *zVarname, int nVarname, | | | | 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 | ** array variable. If the variable is a scalar, *pzInner is set to 0. ** If it is an array variable, (*pzInner, *pnInner) is set to the ** array key name. */ static int thAnalyseVarname( const char *zVarname, int nVarname, const char **pzOuter, /* OUT: Pointer to scalar/array name */ int *pnOuter, /* OUT: Number of bytes at *pzOuter */ const char **pzInner, /* OUT: Pointer to array key (or null) */ int *pnInner, /* OUT: Number of bytes at *pzInner */ int *pisGlobal /* OUT: Set to true if this is a global ref */ ){ const char *zOuter = zVarname; int nOuter; const char *zInner = 0; int nInner = 0; |
︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 1046 1047 | *pnOuter = nOuter; *pzInner = zInner; *pnInner = nInner; *pisGlobal = isGlobal; return TH_OK; } /* ** Input string (zVar, nVar) contains a variable name. This function locates | > > > > > > > > > > > > > > > | > | | | | > > > > > > > | > > > > | > > > > > > | > > | > | | | | > | | | | 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 | *pnOuter = nOuter; *pzInner = zInner; *pnInner = nInner; *pisGlobal = isGlobal; return TH_OK; } /* ** The Find structure is used to return extra information to callers of the ** thFindValue function. The fields within it are populated by thFindValue ** as soon as the necessary information is available. Callers should check ** each field of interest upon return. */ struct Find { Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */ Th_HashEntry *pElemEntry; /* Pointer to array element hash entry, if any */ const char *zElem; /* Name of array element, if applicable */ int nElem; /* Length of array element name, if applicable */ }; typedef struct Find Find; /* ** Input string (zVar, nVar) contains a variable name. This function locates ** the Th_Variable structure associated with the named variable. The ** variable name may be a global or local scalar or array variable ** ** If the create argument is non-zero and the named variable does not exist ** it is created. Otherwise, an error is left in the interpreter result ** and NULL returned. ** ** If the arrayok argument is false and the named variable is an array, ** an error is left in the interpreter result and NULL returned. If ** arrayok is true an array name is Ok. */ static Th_Variable *thFindValue( Th_Interp *interp, const char *zVar, /* Pointer to variable name */ int nVar, /* Number of bytes at nVar */ int create, /* If true, create the variable if not found */ int arrayok, /* If true, an array is Ok. Otherwise array==error */ int noerror, /* If false, set interpreter result to error */ Find *pFind /* If non-zero, place output here */ ){ const char *zOuter; int nOuter; const char *zInner; int nInner; int isGlobal; Th_HashEntry *pEntry; Th_Frame *pFrame = interp->pFrame; Th_Variable *pValue; thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal); if( pFind ){ memset(pFind, 0, sizeof(Find)); pFind->zElem = zInner; pFind->nElem = nInner; } if( isGlobal ){ while( pFrame->pCaller ) pFrame = pFrame->pCaller; } pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); assert(pEntry || create<=0); if( pFind ){ pFind->pValueEntry = pEntry; } if( !pEntry ){ goto no_such_var; } pValue = (Th_Variable *)pEntry->pData; if( !pValue ){ assert(create); pValue = Th_Malloc(interp, sizeof(Th_Variable)); pValue->nRef = 1; pEntry->pData = (void *)pValue; } if( zInner ){ if( pValue->zData ){ if( !noerror ){ Th_ErrorMessage(interp, "variable is a scalar:", zOuter, nOuter); } return 0; } if( !pValue->pHash ){ if( !create ){ goto no_such_var; } pValue->pHash = Th_HashNew(interp); } pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); assert(pEntry || create<=0); if( pFind ){ pFind->pElemEntry = pEntry; } if( !pEntry ){ goto no_such_var; } pValue = (Th_Variable *)pEntry->pData; if( !pValue ){ assert(create); pValue = Th_Malloc(interp, sizeof(Th_Variable)); pValue->nRef = 1; pEntry->pData = (void *)pValue; } }else{ if( pValue->pHash && !arrayok ){ if( !noerror ){ Th_ErrorMessage(interp, "variable is an array:", zOuter, nOuter); } return 0; } } return pValue; no_such_var: if( !noerror ){ Th_ErrorMessage(interp, "no such variable:", zVar, nVar); } return 0; } /* ** String (zVar, nVar) must contain the name of a scalar variable or ** array member. Look up the variable, store its current value in ** the interpreter result and return TH_OK. ** ** If the named variable does not exist, return TH_ERROR and leave ** an error message in the interpreter result. */ int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ Th_Variable *pValue; pValue = thFindValue(interp, zVar, nVar, 0, 0, 0, 0); if( !pValue ){ return TH_ERROR; } if( !pValue->zData ){ Th_ErrorMessage(interp, "no such variable:", zVar, nVar); return TH_ERROR; } return Th_SetResult(interp, pValue->zData, pValue->nData); } /* ** Return true if variable (zVar, nVar) exists. */ int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){ Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0); return pValue && (pValue->zData || pValue->pHash); } /* ** String (zVar, nVar) must contain the name of a scalar variable or ** array member. If the variable does not exist it is created. The ** variable is set to the value supplied in string (zValue, nValue). ** ** If (zVar, nVar) refers to an existing array, TH_ERROR is returned ** and an error message left in the interpreter result. */ int Th_SetVar( Th_Interp *interp, const char *zVar, int nVar, const char *zValue, int nValue ){ Th_Variable *pValue; pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0); if( !pValue ){ return TH_ERROR; } if( nValue<0 ){ nValue = th_strlen(zValue); } |
︙ | ︙ | |||
1199 1200 1201 1202 1203 1204 1205 | /* ** Create a variable link so that accessing variable (zLocal, nLocal) is ** the same as accessing variable (zLink, nLink) in stack frame iFrame. */ int Th_LinkVar( Th_Interp *interp, /* Interpreter */ | | | | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | | | | > > > > > > > > | | 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 | /* ** Create a variable link so that accessing variable (zLocal, nLocal) is ** the same as accessing variable (zLink, nLink) in stack frame iFrame. */ int Th_LinkVar( Th_Interp *interp, /* Interpreter */ const char *zLocal, int nLocal, /* Local varname */ int iFrame, /* Stack frame of linked var */ const char *zLink, int nLink /* Linked varname */ ){ Th_Frame *pSavedFrame = interp->pFrame; Th_Frame *pFrame; Th_HashEntry *pEntry; Th_Variable *pValue; pFrame = getFrame(interp, iFrame); if( !pFrame ){ return TH_ERROR; } pSavedFrame = interp->pFrame; interp->pFrame = pFrame; pValue = thFindValue(interp, zLink, nLink, 1, 1, 0, 0); interp->pFrame = pSavedFrame; pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); if( pEntry->pData ){ Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); return TH_ERROR; } pEntry->pData = (void *)pValue; pValue->nRef++; return TH_OK; } /* ** Input string (zVar, nVar) must contain the name of a scalar variable, ** an array, or an array member. If the identified variable exists, it ** is deleted and TH_OK returned. Otherwise, an error message is left ** in the interpreter result and TH_ERROR is returned. */ int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ Find find; Th_Variable *pValue; Th_HashEntry *pEntry; int rc = TH_ERROR; pValue = thFindValue(interp, zVar, nVar, 0, 1, 0, &find); if( !pValue ){ return rc; } if( pValue->zData || pValue->pHash ){ rc = TH_OK; }else { Th_ErrorMessage(interp, "no such variable:", zVar, nVar); } /* ** The variable may be shared by more than one frame; therefore, make sure ** it is actually freed prior to freeing the parent structure. The values ** for the variable must be freed now so the variable appears undefined in ** all frames. The hash entry in the current frame must also be deleted ** now; otherwise, if the current stack frame is later popped, it will try ** to delete a variable which has already been freed. */ if( find.zElem ){ pEntry = find.pElemEntry; }else{ pEntry = find.pValueEntry; } assert( pEntry ); assert( pValue ); if( thFreeVariable(pEntry, (void *)interp) ){ if( find.zElem ){ Th_Variable *pValue2 = find.pValueEntry->pData; Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1); }else if( pEntry->pData ){ Th_Free(interp, pEntry->pData); pEntry->pData = 0; } }else{ if( pValue->zData ){ Th_Free(interp, pValue->zData); pValue->zData = 0; } if( pValue->pHash ){ Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp); Th_HashDelete(interp, pValue->pHash); pValue->pHash = 0; } if( find.zElem ){ Th_Variable *pValue2 = find.pValueEntry->pData; Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1); } } if( !find.zElem ){ Th_HashFind(interp, interp->pFrame->paVar, zVar, nVar, -1); } return rc; } /* ** Return an allocated buffer containing a copy of string (z, n). The ** caller is responsible for eventually calling Th_Free() to free ** the returned buffer. */ |
︙ | ︙ | |||
1288 1289 1290 1291 1292 1293 1294 | */ int Th_ErrorMessage(Th_Interp *interp, const char *zPre, const char *z, int n){ if( interp ){ char *zRes = 0; int nRes = 0; Th_SetVar(interp, (char *)"::th_stack_trace", -1, 0, 0); | | | 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 | */ int Th_ErrorMessage(Th_Interp *interp, const char *zPre, const char *z, int n){ if( interp ){ char *zRes = 0; int nRes = 0; Th_SetVar(interp, (char *)"::th_stack_trace", -1, 0, 0); Th_StringAppend(interp, &zRes, &nRes, zPre, -1); if( zRes[nRes-1]=='"' ){ Th_StringAppend(interp, &zRes, &nRes, z, n); Th_StringAppend(interp, &zRes, &nRes, (const char *)"\"", 1); }else{ Th_StringAppend(interp, &zRes, &nRes, (const char *)" ", 1); Th_StringAppend(interp, &zRes, &nRes, z, n); |
︙ | ︙ | |||
1370 1371 1372 1373 1374 1375 1376 | return zResult; }else{ return (char *)Th_Malloc(pInterp, 1); } } | | | | | | 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 | return zResult; }else{ return (char *)Th_Malloc(pInterp, 1); } } /* ** Wrappers around the supplied malloc() and free() */ void *Th_Malloc(Th_Interp *pInterp, int nByte){ void *p = pInterp->pVtab->xMalloc(nByte); if( p ){ memset(p, 0, nByte); } return p; } void Th_Free(Th_Interp *pInterp, void *z){ if( z ){ pInterp->pVtab->xFree(z); } } /* ** Install a new th1 command. ** ** If a command of the same name already exists, it is deleted automatically. */ int Th_CreateCommand( Th_Interp *interp, const char *zName, /* New command name */ Th_CommandProc xProc, /* Command callback proc */ void *pContext, /* Value to pass as second arg to xProc */ void (*xDel)(Th_Interp *, void *) /* Command destructor callback */ ){ Th_HashEntry *pEntry; Th_Command *pCommand; |
︙ | ︙ | |||
1414 1415 1416 1417 1418 1419 1420 | }else{ pCommand = Th_Malloc(interp, sizeof(Th_Command)); } pCommand->xProc = xProc; pCommand->pContext = pContext; pCommand->xDel = xDel; pEntry->pData = (void *)pCommand; | | | | | | | | 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 | }else{ pCommand = Th_Malloc(interp, sizeof(Th_Command)); } pCommand->xProc = xProc; pCommand->pContext = pContext; pCommand->xDel = xDel; pEntry->pData = (void *)pCommand; return TH_OK; } /* ** Rename the existing command (zName, nName) to (zNew, nNew). If nNew is 0, ** the command is deleted instead of renamed. ** ** If successful, TH_OK is returned. If command zName does not exist, or ** if command zNew already exists, an error message is left in the ** interpreter result and TH_ERROR is returned. */ int Th_RenameCommand( Th_Interp *interp, const char *zName, /* Existing command name */ int nName, /* Number of bytes at zName */ const char *zNew, /* New command name */ int nNew /* Number of bytes at zNew */ ){ Th_HashEntry *pEntry; Th_HashEntry *pNewEntry; pEntry = Th_HashFind(interp, interp->paCmd, zName, nName, 0); if( !pEntry ){ |
︙ | ︙ | |||
1488 1489 1490 1491 1492 1493 1494 | ** Split a th1 list into its component elements. The list to split is ** passed via arguments (zList, nList). If successful, TH_OK is returned. ** If an error occurs (if (zList, nList) is not a valid list) an error ** message is left in the interpreter result and TH_ERROR returned. ** ** If successful, *pnCount is set to the number of elements in the list. ** panElem is set to point at an array of *pnCount integers - the lengths | | | 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 | ** Split a th1 list into its component elements. The list to split is ** passed via arguments (zList, nList). If successful, TH_OK is returned. ** If an error occurs (if (zList, nList) is not a valid list) an error ** message is left in the interpreter result and TH_ERROR returned. ** ** If successful, *pnCount is set to the number of elements in the list. ** panElem is set to point at an array of *pnCount integers - the lengths ** of the element values. *pazElem is set to point at an array of ** pointers to buffers containing the array element's data. ** ** To free the arrays allocated at *pazElem and *panElem, the caller ** should call Th_Free() on *pazElem only. Exactly one such call to ** Th_Free() must be made per call to Th_SplitList(). ** ** Example: |
︙ | ︙ | |||
1514 1515 1516 1517 1518 1519 1520 | ** } ** ** Th_Free(interp, azElem); ** */ int Th_SplitList( Th_Interp *interp, | | | | | | | | | 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 | ** } ** ** Th_Free(interp, azElem); ** */ int Th_SplitList( Th_Interp *interp, const char *zList, /* Pointer to buffer containing list */ int nList, /* Number of bytes at zList */ char ***pazElem, /* OUT: Array of pointers to element data */ int **panElem, /* OUT: Array of element data lengths */ int *pnCount /* OUT: Number of elements in list */ ){ int rc; interp->isListMode = 1; rc = thSplitList(interp, zList, nList, pazElem, panElem, pnCount); interp->isListMode = 0; if( rc ){ Th_ErrorMessage(interp, "Expected list, got: \"", zList, nList); } return rc; } /* ** Append a new element to an existing th1 list. The element to append ** to the list is (zElem, nElem). ** ** A pointer to the existing list must be stored at *pzList when this ** function is called. The length must be stored in *pnList. The value ** of *pzList must either be NULL (in which case *pnList must be 0), or ** a pointer to memory obtained from Th_Malloc(). ** ** This function calls Th_Free() to free the buffer at *pzList and sets ** *pzList to point to a new buffer containing the new list value. *pnList ** is similarly updated before returning. The return value is always TH_OK. ** ** Example: ** ** char *zList = 0; ** int nList = 0; ** for (...) { ** char *zElem = <some expression>; ** Th_ListAppend(interp, &zList, &nList, zElem, -1); ** } ** Th_SetResult(interp, zList, nList); ** Th_Free(interp, zList); ** */ int Th_ListAppend( Th_Interp *interp, /* Interpreter context */ char **pzList, /* IN/OUT: Ptr to ptr to list */ int *pnList, /* IN/OUT: Current length of *pzList */ const char *zElem, /* Data to append */ int nElem /* Length of nElem */ ){ Buffer output; int i; int hasSpecialChar = 0; int hasEscapeChar = 0; |
︙ | ︙ | |||
1612 1613 1614 1615 1616 1617 1618 | /* ** Append a new element to an existing th1 string. This function uses ** the same interface as the Th_ListAppend() function. */ int Th_StringAppend( Th_Interp *interp, /* Interpreter context */ | | | | 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 | /* ** Append a new element to an existing th1 string. This function uses ** the same interface as the Th_ListAppend() function. */ int Th_StringAppend( Th_Interp *interp, /* Interpreter context */ char **pzStr, /* IN/OUT: Ptr to ptr to list */ int *pnStr, /* IN/OUT: Current length of *pzStr */ const char *zElem, /* Data to append */ int nElem /* Length of nElem */ ){ char *zNew; int nNew; if( nElem<0 ){ nElem = th_strlen(zElem); |
︙ | ︙ | |||
1636 1637 1638 1639 1640 1641 1642 | Th_Free(interp, *pzStr); *pzStr = zNew; *pnStr = nNew; return TH_OK; } | | | | 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 | Th_Free(interp, *pzStr); *pzStr = zNew; *pnStr = nNew; return TH_OK; } /* ** Delete an interpreter. */ void Th_DeleteInterp(Th_Interp *interp){ assert(interp->pFrame); assert(0==interp->pFrame->pCaller); /* Delete the contents of the global frame. */ thPopFrame(interp); /* Delete any result currently stored in the interpreter. */ Th_SetResult(interp, 0, 0); /* Delete all registered commands and the command hash-table itself. */ Th_HashIterate(interp, interp->paCmd, thFreeCommand, (void *)interp); Th_HashDelete(interp, interp->paCmd); /* Delete the interpreter structure itself. */ Th_Free(interp, (void *)interp); } /* ** Create a new interpreter. */ Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){ Th_Interp *p; /* Allocate and initialise the interpreter and the global frame */ p = pVtab->xMalloc(sizeof(Th_Interp) + sizeof(Th_Frame)); |
︙ | ︙ | |||
1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 | /* ** These two types are used only by the expression module, where ** the expression module means the Th_Expr() and exprXXX() functions. */ typedef struct Operator Operator; struct Operator { const char *zOp; int eOp; int iPrecedence; int eArgType; }; typedef struct Expr Expr; struct Expr { Operator *pOp; Expr *pParent; Expr *pLeft; Expr *pRight; | > | | 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 | /* ** These two types are used only by the expression module, where ** the expression module means the Th_Expr() and exprXXX() functions. */ typedef struct Operator Operator; struct Operator { const char *zOp; int nOp; int eOp; int iPrecedence; int eArgType; }; typedef struct Expr Expr; struct Expr { Operator *pOp; Expr *pParent; Expr *pLeft; Expr *pRight; char *zValue; /* Pointer to literal value */ int nValue; /* Length of literal value buffer */ }; /* Unary operators */ #define OP_UNARY_MINUS 2 #define OP_UNARY_PLUS 3 #define OP_BITWISE_NOT 4 |
︙ | ︙ | |||
1737 1738 1739 1740 1741 1742 1743 | */ #define ARG_INTEGER 1 #define ARG_NUMBER 2 #define ARG_STRING 3 static Operator aOperator[] = { | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 | */ #define ARG_INTEGER 1 #define ARG_NUMBER 2 #define ARG_STRING 3 static Operator aOperator[] = { {"(", 1, OP_OPEN_BRACKET, -1, 0}, {")", 1, OP_CLOSE_BRACKET, -1, 0}, /* Note: all unary operators have (iPrecedence==1) */ {"-", 1, OP_UNARY_MINUS, 1, ARG_NUMBER}, {"+", 1, OP_UNARY_PLUS, 1, ARG_NUMBER}, {"~", 1, OP_BITWISE_NOT, 1, ARG_INTEGER}, {"!", 1, OP_LOGICAL_NOT, 1, ARG_INTEGER}, /* Binary operators. It is important to the parsing in Th_Expr() that * the two-character symbols ("==") appear before the one-character * ones ("="). And that the priorities of all binary operators are * integers between 2 and 12. */ {"<<", 2, OP_LEFTSHIFT, 4, ARG_INTEGER}, {">>", 2, OP_RIGHTSHIFT, 4, ARG_INTEGER}, {"<=", 2, OP_LE, 5, ARG_NUMBER}, {">=", 2, OP_GE, 5, ARG_NUMBER}, {"==", 2, OP_EQ, 6, ARG_NUMBER}, {"!=", 2, OP_NE, 6, ARG_NUMBER}, {"eq", 2, OP_SEQ, 7, ARG_STRING}, {"ne", 2, OP_SNE, 7, ARG_STRING}, {"&&", 2, OP_LOGICAL_AND, 11, ARG_INTEGER}, {"||", 2, OP_LOGICAL_OR, 12, ARG_INTEGER}, {"*", 1, OP_MULTIPLY, 2, ARG_NUMBER}, {"/", 1, OP_DIVIDE, 2, ARG_NUMBER}, {"%", 1, OP_MODULUS, 2, ARG_INTEGER}, {"+", 1, OP_ADD, 3, ARG_NUMBER}, {"-", 1, OP_SUBTRACT, 3, ARG_NUMBER}, {"<", 1, OP_LT, 5, ARG_NUMBER}, {">", 1, OP_GT, 5, ARG_NUMBER}, {"&", 1, OP_BITWISE_AND, 8, ARG_INTEGER}, {"^", 1, OP_BITWISE_XOR, 9, ARG_INTEGER}, {"|", 1, OP_BITWISE_OR, 10, ARG_INTEGER}, {0,0,0,0,0} }; /* ** The first part of the string (zInput,nInput) contains an integer. ** Set *pnVarname to the number of bytes in the numeric string. */ static int thNextInteger( Th_Interp *interp, const char *zInput, int nInput, int *pnLiteral ){ int i; int (*isdigit)(char) = th_isdigit; char c; if( nInput<2) return TH_ERROR; assert(zInput[0]=='0'); c = zInput[1]; if( c>='A' && c<='Z' ) c += 'a' - 'A'; if( c=='x' ){ isdigit = th_ishexdig; }else if( c!='o' && c!='b' ){ return TH_ERROR; } for(i=2; i<nInput; i++){ c = zInput[i]; if( !isdigit(c) ){ break; } } *pnLiteral = i; return TH_OK; } /* ** The first part of the string (zInput,nInput) contains a number. ** Set *pnVarname to the number of bytes in the numeric string. */ static int thNextNumber( Th_Interp *interp, const char *zInput, int nInput, int *pnLiteral ){ int i = 0; int seenDot = 0; for(; i<nInput; i++){ char c = zInput[i]; if( (seenDot || c!='.') && !th_isdigit(c) ) break; if( c=='.' ) seenDot = 1; } *pnLiteral = i; return TH_OK; } |
︙ | ︙ | |||
1853 1854 1855 1856 1857 1858 1859 | if( rc==TH_OK ){ eArgType = pExpr->pOp->eArgType; if( eArgType==ARG_NUMBER ){ if( (zLeft==0 || TH_OK==Th_ToInt(0, zLeft, nLeft, &iLeft)) && (zRight==0 || TH_OK==Th_ToInt(0, zRight, nRight, &iRight)) ){ eArgType = ARG_INTEGER; | | | | | > | | > > | > > > > > > | > | | | | | | | | > > > > | 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 | if( rc==TH_OK ){ eArgType = pExpr->pOp->eArgType; if( eArgType==ARG_NUMBER ){ if( (zLeft==0 || TH_OK==Th_ToInt(0, zLeft, nLeft, &iLeft)) && (zRight==0 || TH_OK==Th_ToInt(0, zRight, nRight, &iRight)) ){ eArgType = ARG_INTEGER; }else if( (zLeft && TH_OK!=Th_ToDouble(interp, zLeft, nLeft, &fLeft)) || (zRight && TH_OK!=Th_ToDouble(interp, zRight, nRight, &fRight)) ){ /* A type error. */ rc = TH_ERROR; } }else if( eArgType==ARG_INTEGER ){ rc = Th_ToInt(interp, zLeft, nLeft, &iLeft); if( rc==TH_OK && zRight ){ rc = Th_ToInt(interp, zRight, nRight, &iRight); } } } if( rc==TH_OK && eArgType==ARG_INTEGER ){ int iRes = 0; switch( pExpr->pOp->eOp ) { case OP_MULTIPLY: iRes = iLeft*iRight; break; case OP_DIVIDE: if( !iRight ){ Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft); rc = TH_ERROR; goto finish; } iRes = iLeft/iRight; break; case OP_MODULUS: if( !iRight ){ Th_ErrorMessage(interp, "Modulo by 0:", zLeft, nLeft); rc = TH_ERROR; goto finish; } iRes = iLeft%iRight; break; case OP_ADD: iRes = iLeft+iRight; break; case OP_SUBTRACT: iRes = iLeft-iRight; break; case OP_LEFTSHIFT: iRes = iLeft<<iRight; break; case OP_RIGHTSHIFT: iRes = iLeft>>iRight; break; case OP_LT: iRes = iLeft<iRight; break; case OP_GT: iRes = iLeft>iRight; break; case OP_LE: iRes = iLeft<=iRight; break; case OP_GE: iRes = iLeft>=iRight; break; case OP_EQ: iRes = iLeft==iRight; break; case OP_NE: iRes = iLeft!=iRight; break; case OP_BITWISE_AND: iRes = iLeft&iRight; break; case OP_BITWISE_XOR: iRes = iLeft^iRight; break; case OP_BITWISE_OR: iRes = iLeft|iRight; break; case OP_LOGICAL_AND: iRes = iLeft&&iRight; break; case OP_LOGICAL_OR: iRes = iLeft||iRight; break; case OP_UNARY_MINUS: iRes = -iLeft; break; case OP_UNARY_PLUS: iRes = +iLeft; break; case OP_BITWISE_NOT: iRes = ~iLeft; break; case OP_LOGICAL_NOT: iRes = !iLeft; break; default: assert(!"Internal error"); } Th_SetResultInt(interp, iRes); }else if( rc==TH_OK && eArgType==ARG_NUMBER ){ switch( pExpr->pOp->eOp ) { case OP_MULTIPLY: Th_SetResultDouble(interp, fLeft*fRight); break; case OP_DIVIDE: if( fRight==0.0 ){ Th_ErrorMessage(interp, "Divide by 0:", zLeft, nLeft); rc = TH_ERROR; goto finish; } Th_SetResultDouble(interp, fLeft/fRight); break; case OP_ADD: Th_SetResultDouble(interp, fLeft+fRight); break; case OP_SUBTRACT: Th_SetResultDouble(interp, fLeft-fRight); break; case OP_LT: Th_SetResultInt(interp, fLeft<fRight); break; case OP_GT: Th_SetResultInt(interp, fLeft>fRight); break; case OP_LE: Th_SetResultInt(interp, fLeft<=fRight); break; case OP_GE: Th_SetResultInt(interp, fLeft>=fRight); break; case OP_EQ: Th_SetResultInt(interp, fLeft==fRight); break; case OP_NE: Th_SetResultInt(interp, fLeft!=fRight); break; case OP_UNARY_MINUS: Th_SetResultDouble(interp, -fLeft); break; case OP_UNARY_PLUS: Th_SetResultDouble(interp, +fLeft); break; default: assert(!"Internal error"); } }else if( rc==TH_OK ){ int iEqual = 0; assert( eArgType==ARG_STRING ); if( nRight==nLeft && 0==memcmp(zRight, zLeft, nRight) ){ iEqual = 1; } switch( pExpr->pOp->eOp ) { case OP_SEQ: Th_SetResultInt(interp, iEqual); break; case OP_SNE: Th_SetResultInt(interp, !iEqual); break; default: assert(!"Internal error"); } } finish: Th_Free(interp, zLeft); Th_Free(interp, zRight); } return rc; } |
︙ | ︙ | |||
1956 1957 1958 1959 1960 1961 1962 | assert(nToken>0); #define ISTERM(x) (apToken[x] && (!apToken[x]->pOp || apToken[x]->pLeft)) for(jj=0; jj<nToken; jj++){ if( apToken[jj]->pOp && apToken[jj]->pOp->eOp==OP_OPEN_BRACKET ){ int nNest = 1; | | | 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 | assert(nToken>0); #define ISTERM(x) (apToken[x] && (!apToken[x]->pOp || apToken[x]->pLeft)) for(jj=0; jj<nToken; jj++){ if( apToken[jj]->pOp && apToken[jj]->pOp->eOp==OP_OPEN_BRACKET ){ int nNest = 1; int iLeft = jj; for(jj++; jj<nToken; jj++){ Operator *pOp = apToken[jj]->pOp; if( pOp && pOp->eOp==OP_OPEN_BRACKET ) nNest++; if( pOp && pOp->eOp==OP_CLOSE_BRACKET ) nNest--; if( nNest==0 ) break; } |
︙ | ︙ | |||
1982 1983 1984 1985 1986 1987 1988 | } } } iLeft = 0; for(jj=nToken-1; jj>=0; jj--){ if( apToken[jj] ){ | | > < < | | 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 | } } } iLeft = 0; for(jj=nToken-1; jj>=0; jj--){ if( apToken[jj] ){ if( apToken[jj]->pOp && apToken[jj]->pOp->iPrecedence==1 && iLeft>0 && ISTERM(iLeft) ){ apToken[jj]->pLeft = apToken[iLeft]; apToken[jj]->pLeft->pParent = apToken[jj]; apToken[iLeft] = 0; } iLeft = jj; } } for(i=2; i<=12; i++){ iLeft = -1; for(jj=0; jj<nToken; jj++){ Expr *pToken = apToken[jj]; if( apToken[jj] ){ if( pToken->pOp && !pToken->pLeft && pToken->pOp->iPrecedence==i ){ int iRight = jj+1; for(; !apToken[iRight] && iRight<nToken; iRight++); if( iRight==nToken || iLeft<0 || !ISTERM(iRight) || !ISTERM(iLeft) ){ return TH_ERROR; } pToken->pLeft = apToken[iLeft]; apToken[iLeft] = 0; pToken->pLeft->pParent = pToken; pToken->pRight = apToken[iRight]; |
︙ | ︙ | |||
2030 2031 2032 2033 2034 2035 2036 | } /* ** Parse a string containing a TH expression to a list of tokens. */ static int exprParse( Th_Interp *interp, /* Interpreter to leave error message in */ | | > > > > > > | | | 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 | } /* ** Parse a string containing a TH expression to a list of tokens. */ static int exprParse( Th_Interp *interp, /* Interpreter to leave error message in */ const char *zExpr, /* Pointer to input string */ int nExpr, /* Number of bytes at zExpr */ Expr ***papToken, /* OUT: Array of tokens. */ int *pnToken /* OUT: Size of token array */ ){ int i; int rc = TH_OK; int nNest = 0; int nToken = 0; Expr **apToken = 0; for(i=0; rc==TH_OK && i<nExpr; ){ char c = zExpr[i]; if( th_isspace(c) ){ /* White-space */ i++; }else{ Expr *pNew = (Expr *)Th_Malloc(interp, sizeof(Expr)); const char *z = &zExpr[i]; switch (c) { case '0': if( thNextInteger(interp, z, nExpr-i, &pNew->nValue)==TH_OK ){ break; } /* fall through */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': thNextNumber(interp, z, nExpr-i, &pNew->nValue); break; case '$': thNextVarname(interp, z, nExpr-i, &pNew->nValue); break; |
︙ | ︙ | |||
2077 2078 2079 2080 2081 2082 2083 | pNew->nValue = iEnd+1-i; } break; } default: { int j; | > | | > > > > > > > > > > > > > > > > > > > > | < | | > > > > | | | 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 | pNew->nValue = iEnd+1-i; } break; } default: { int j; const char *zOp; for(j=0; (zOp=aOperator[j].zOp); j++){ int nOp = aOperator[j].nOp; int nRemain = nExpr - i; int isMatch = 0; if( nRemain>=nOp && 0==memcmp(zOp, &zExpr[i], nOp) ){ isMatch = 1; } if( isMatch ){ if( aOperator[j].eOp==OP_CLOSE_BRACKET ){ nNest--; }else if( nRemain>nOp ){ if( aOperator[j].eOp==OP_OPEN_BRACKET ){ nNest++; } }else{ /* ** This is not really a match because this operator cannot ** legally appear at the end of the string. */ isMatch = 0; } } if( nToken>0 && aOperator[j].iPrecedence==1 ){ Expr *pPrev = apToken[nToken-1]; if( !pPrev->pOp || pPrev->pOp->eOp==OP_CLOSE_BRACKET ){ continue; } } if( isMatch ){ pNew->pOp = &aOperator[j]; i += nOp; break; } } } } if( pNew->pOp || pNew->nValue ){ if( pNew->nValue ){ /* A terminal. Copy the string value. */ assert( !pNew->pOp ); pNew->zValue = Th_Malloc(interp, pNew->nValue); memcpy(pNew->zValue, z, pNew->nValue); i += pNew->nValue; } if( (nToken%16)==0 ){ /* Grow the apToken array. */ Expr **apTokenOld = apToken; apToken = Th_Malloc(interp, sizeof(Expr *)*(nToken+16)); memcpy(apToken, apTokenOld, sizeof(Expr *)*nToken); } /* Put the new token at the end of the apToken array */ apToken[nToken] = pNew; nToken++; }else{ Th_Free(interp, pNew); rc = TH_ERROR; } } } if( nNest!=0 ){ rc = TH_ERROR; } *papToken = apToken; *pnToken = nToken; return rc; } /* ** Evaluate the string (zExpr, nExpr) as a Th expression. Store ** the result in the interpreter interp and return TH_OK if ** successful. If an error occurs, store an error message in ** the interpreter result and return an error code. */ int Th_Expr(Th_Interp *interp, const char *zExpr, int nExpr){ int rc; /* Return Code */ int i; /* Loop counter */ int nToken = 0; Expr **apToken = 0; if( nExpr<0 ){ nExpr = th_strlen(zExpr); } /* Parse the expression to a list of tokens. */ rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken); /* If the parsing was successful, create an expression tree from ** the parsed list of tokens. If successful, apToken[0] is set ** to point to the root of the expression tree. */ if( rc==TH_OK ){ rc = exprMakeTree(interp, apToken, nToken); } if( rc!=TH_OK ){ Th_ErrorMessage(interp, "syntax error in expression: \"", zExpr, nExpr); |
︙ | ︙ | |||
2185 2186 2187 2188 2189 2190 2191 | return p; } /* ** Iterate through all values currently stored in the hash table. Invoke ** the callback function xCallback for each entry. The second argument ** passed to xCallback is a copy of the fourth argument passed to this | | > | | | | > | | | | 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 | return p; } /* ** Iterate through all values currently stored in the hash table. Invoke ** the callback function xCallback for each entry. The second argument ** passed to xCallback is a copy of the fourth argument passed to this ** function. The return value from the callback function xCallback is ** ignored. */ void Th_HashIterate( Th_Interp *interp, Th_Hash *pHash, int (*xCallback)(Th_HashEntry *pEntry, void *pContext), void *pContext ){ int i; for(i=0; i<TH_HASHSIZE; i++){ Th_HashEntry *pEntry; Th_HashEntry *pNext; for(pEntry=pHash->a[i]; pEntry; pEntry=pNext){ pNext = pEntry->pNext; xCallback(pEntry, pContext); } } } /* ** Helper function for Th_HashDelete(). Always returns non-zero. */ static int xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ Th_Free((Th_Interp *)pContext, (void *)pEntry); return 1; } /* ** Free a hash-table previously allocated by Th_HashNew(). */ void Th_HashDelete(Th_Interp *interp, Th_Hash *pHash){ if( pHash ){ Th_HashIterate(interp, pHash, xFreeHashEntry, (void *)interp); Th_Free(interp, pHash); } } /* ** This function is used to insert or delete hash table items, or to ** query a hash table for an existing item. ** ** If parameter op is less than zero, then the hash-table element ** identified by (zKey, nKey) is removed from the hash-table if it ** exists. NULL is returned. ** ** Otherwise, if the hash-table contains an item with key (zKey, nKey), ** a pointer to the associated Th_HashEntry is returned. If parameter ** op is greater than zero, then a new entry is added if one cannot ** be found. If op is zero, then NULL is returned if the item is ** not already present in the hash-table. */ Th_HashEntry *Th_HashFind( Th_Interp *interp, Th_Hash *pHash, const char *zKey, int nKey, int op /* -ve = delete, 0 = find, +ve = insert */ ){ unsigned int iKey = 0; int i; |
︙ | ︙ | |||
2304 2305 2306 2307 2308 2309 2310 | ** '\n' 0x0A ** '\v' 0x0B ** '\f' 0x0C ** '\r' 0x0D ** ** Whitespace characters have the 0x01 flag set. Decimal digits have the ** 0x2 flag set. Single byte printable characters have the 0x4 flag set. | | > | | | | 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 | ** '\n' 0x0A ** '\v' 0x0B ** '\f' 0x0C ** '\r' 0x0D ** ** Whitespace characters have the 0x01 flag set. Decimal digits have the ** 0x2 flag set. Single byte printable characters have the 0x4 flag set. ** Alphabet characters have the 0x8 bit set. Hexadecimal digits have the ** 0x20 flag set. ** ** The special list characters have the 0x10 flag set ** ** { } [ ] \ ; ' " ** ** " 0x22 ** */ static unsigned char aCharProp[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, /* 0x0. */ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x1. */ 5, 4, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x2. */ 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 4, 20, 4, 4, 4, 4, /* 0x3. */ 4, 44, 44, 44, 44, 44, 44, 12, 12, 12, 12, 12, 12, 12, 12, 12, /* 0x4. */ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 20, 20, 20, 4, 4, /* 0x5. */ 4, 44, 44, 44, 44, 44, 44, 12, 12, 12, 12, 12, 12, 12, 12, 12, /* 0x6. */ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 20, 4, 20, 4, 4, /* 0x7. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x8. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x9. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC. */ |
︙ | ︙ | |||
2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 | } int th_isspecial(char c){ return (aCharProp[(unsigned char)c] & 0x11); } int th_isalnum(char c){ return (aCharProp[(unsigned char)c] & 0x0A); } #ifndef LONGDOUBLE_TYPE # define LONGDOUBLE_TYPE long double #endif | > > > > > > > > > > > > < | 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 | } int th_isspecial(char c){ return (aCharProp[(unsigned char)c] & 0x11); } int th_isalnum(char c){ return (aCharProp[(unsigned char)c] & 0x0A); } int th_isalpha(char c){ return (aCharProp[(unsigned char)c] & 0x08); } int th_ishexdig(char c){ return (aCharProp[(unsigned char)c] & 0x20); } int th_isoctdig(char c){ return ((c|7) == '7'); } int th_isbindig(char c){ return ((c|1) == '1'); } #ifndef LONGDOUBLE_TYPE # define LONGDOUBLE_TYPE long double #endif /* ** Return TRUE if z is a pure numeric string. Return FALSE if the ** string contains any character which is not part of a number. If ** the string is numeric and contains the '.' character, set *realnum ** to TRUE (otherwise FALSE). |
︙ | ︙ | |||
2456 2457 2458 2459 2460 2461 2462 | } *pResult = sign<0 ? -v1 : v1; return z - zBegin; } /* ** Try to convert the string passed as arguments (z, n) to an integer. | | | | > > | > > > > > > > > > > > > > > > > | > > > > > > > | | | | | | | | 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 | } *pResult = sign<0 ? -v1 : v1; return z - zBegin; } /* ** Try to convert the string passed as arguments (z, n) to an integer. ** If successful, store the result in *piOut and return TH_OK. ** ** If the string cannot be converted to an integer, return TH_ERROR. ** If the interp argument is not NULL, leave an error message in the ** interpreter result too. */ int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){ int i = 0; int iOut = 0; int base = 10; int (*isdigit)(char) = th_isdigit; if( n<0 ){ n = th_strlen(z); } if( n>1 && (z[0]=='-' || z[0]=='+') ){ i = 1; } if( (n-i)>2 && z[i]=='0' ){ if( z[i+1]=='x' || z[i+1]=='X' ){ i += 2; base = 16; isdigit = th_ishexdig; }else if( z[i+1]=='o' || z[i+1]=='O' ){ i += 2; base = 8; isdigit = th_isoctdig; }else if( z[i+1]=='b' || z[i+1]=='B' ){ i += 2; base = 2; isdigit = th_isbindig; } } for(; i<n; i++){ char c = z[i]; if( !isdigit(c) ){ Th_ErrorMessage(interp, "expected integer, got: \"", z, n); return TH_ERROR; } if( c>='a' ){ c -= 'a'-10; }else if( c>='A' ){ c -= 'A'-10; }else{ c -= '0'; } iOut = iOut * base + c; } if( n>0 && z[0]=='-' ){ iOut *= -1; } *piOut = iOut; return TH_OK; } /* ** Try to convert the string passed as arguments (z, n) to a double. ** If successful, store the result in *pfOut and return TH_OK. ** ** If the string cannot be converted to a double, return TH_ERROR. ** If the interp argument is not NULL, leave an error message in the ** interpreter result too. */ int Th_ToDouble( Th_Interp *interp, const char *z, int n, double *pfOut ){ if( !sqlite3IsNumber((const char *)z, 0) ){ Th_ErrorMessage(interp, "expected number, got: \"", z, n); return TH_ERROR; } |
︙ | ︙ | |||
2526 2527 2528 2529 2530 2531 2532 | char *z = &zBuf[32]; if( iVal<0 ){ isNegative = 1; iVal = iVal * -1; } *(--z) = '\0'; | | | | | | | | | | | 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 | char *z = &zBuf[32]; if( iVal<0 ){ isNegative = 1; iVal = iVal * -1; } *(--z) = '\0'; *(--z) = (char)(48+((unsigned)iVal%10)); while( (iVal = ((unsigned)iVal/10))>0 ){ *(--z) = (char)(48+((unsigned)iVal%10)); assert(z>zBuf); } if( isNegative ){ *(--z) = '-'; } return Th_SetResult(interp, z, -1); } /* ** Set the result of the interpreter to the th1 representation of ** the double fVal and return TH_OK. */ int Th_SetResultDouble(Th_Interp *interp, double fVal){ int i; /* Iterator variable */ double v = fVal; /* Input value */ char zBuf[128]; /* Output buffer */ char *z = zBuf; /* Output cursor */ int iDot = 0; /* Digit after which to place decimal point */ int iExp = 0; /* Exponent (NN in eNN) */ const char *zExp; /* String representation of iExp */ /* Precision: */ #define INSIGNIFICANT 0.000000000001 #define ROUNDER 0.0000000000005 double insignificant = INSIGNIFICANT; /* If the real value is negative, write a '-' character to the * output and transform v to the corresponding positive number. */ if( v<0.0 ){ *z++ = '-'; v *= -1.0; } /* Normalize v to a value between 1.0 and 10.0. Integer * variable iExp is set to the exponent. i.e the original * value is (v * 10^iExp) (or the negative thereof). */ if( v>0.0 ){ while( (v+ROUNDER)>=10.0 ) { iExp++; v *= 0.1; } while( (v+ROUNDER)<1.0 ) { iExp--; v *= 10.0; } } v += ROUNDER; /* For a small (<12) positive exponent, move the decimal point |
︙ | ︙ |
Changes to src/th.h.
1 2 | /* This header file defines the external interface to the custom Scripting | | > < | | | | | | | | | | | | | | | | | | | | | | > > > > > > > | | > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | /* This header file defines the external interface to the custom Scripting ** Language (TH) interpreter. TH is very similar to Tcl but is not an ** exact clone. */ /* ** Before creating an interpreter, the application must allocate and ** populate an instance of the following structure. It must remain valid ** for the lifetime of the interpreter. */ typedef struct Th_Vtab Th_Vtab; struct Th_Vtab { void *(*xMalloc)(unsigned int); void (*xFree)(void *); }; /* ** Opaque handle for interpeter. */ typedef struct Th_Interp Th_Interp; /* ** Create and delete interpreters. */ Th_Interp * Th_CreateInterp(Th_Vtab *pVtab); void Th_DeleteInterp(Th_Interp *); /* ** Evaluate an TH program in the stack frame identified by parameter ** iFrame, according to the following rules: ** ** * If iFrame is 0, this means the current frame. ** ** * If iFrame is negative, then the nth frame up the stack, where n is ** the absolute value of iFrame. A value of -1 means the calling ** procedure. ** ** * If iFrame is +ve, then the nth frame from the bottom of the stack. ** An iFrame value of 1 means the toplevel (global) frame. */ int Th_Eval(Th_Interp *interp, int iFrame, const char *zProg, int nProg); /* ** Evaluate a TH expression. The result is stored in the ** interpreter result. */ int Th_Expr(Th_Interp *interp, const char *, int); /* ** Access TH variables in the current stack frame. If the variable name ** begins with "::", the lookup is in the top level (global) frame. */ int Th_ExistsVar(Th_Interp *, const char *, int); int Th_GetVar(Th_Interp *, const char *, int); int Th_SetVar(Th_Interp *, const char *, int, const char *, int); int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); int Th_UnsetVar(Th_Interp *, const char *, int); typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); /* ** Register new commands. */ int Th_CreateCommand( Th_Interp *interp, const char *zName, /* int (*xProc)(Th_Interp *, void *, int, const char **, int *), */ Th_CommandProc xProc, void *pContext, void (*xDel)(Th_Interp *, void *) ); /* ** Delete or rename commands. */ int Th_RenameCommand(Th_Interp *, const char *, int, const char *, int); /* ** Push a new stack frame (local variable context) onto the interpreter ** stack, call the function supplied as parameter xCall with the two ** context arguments, ** ** xCall(interp, pContext1, pContext2) ** ** , then pop the frame off of the interpreter stack. The value returned ** by the xCall() function is returned as the result of this function. ** ** This is intended for use by the implementation of commands such as ** those created by [proc]. */ int Th_InFrame(Th_Interp *interp, int (*xCall)(Th_Interp *, void *pContext1, void *pContext2), void *pContext1, void *pContext2 ); /* ** Valid return codes for xProc callbacks. */ #define TH_OK 0 #define TH_ERROR 1 #define TH_BREAK 2 #define TH_RETURN 3 #define TH_CONTINUE 4 /* ** Set and get the interpreter result. */ int Th_SetResult(Th_Interp *, const char *, int); const char *Th_GetResult(Th_Interp *, int *); char *Th_TakeResult(Th_Interp *, int *); /* ** Set an error message as the interpreter result. This also ** sets the global stack-trace variable $::th_stack_trace. */ int Th_ErrorMessage(Th_Interp *, const char *, const char *, int); /* ** Access the memory management functions associated with the specified ** interpreter. */ void *Th_Malloc(Th_Interp *, int); void Th_Free(Th_Interp *, void *); /* ** Functions for handling TH lists. */ int Th_ListAppend(Th_Interp *, char **, int *, const char *, int); int Th_SplitList(Th_Interp *, const char *, int, char ***, int **, int *); int Th_StringAppend(Th_Interp *, char **, int *, const char *, int); /* ** Functions for handling numbers and pointers. */ int Th_ToInt(Th_Interp *, const char *, int, int *); int Th_ToDouble(Th_Interp *, const char *, int, double *); int Th_SetResultInt(Th_Interp *, int); int Th_SetResultDouble(Th_Interp *, double); /* ** Drop in replacements for the corresponding standard library functions. */ int th_strlen(const char *); int th_isdigit(char); int th_isspace(char); int th_isalnum(char); int th_isalpha(char); int th_isspecial(char); int th_ishexdig(char); int th_isoctdig(char); int th_isbindig(char); char *th_strdup(Th_Interp *interp, const char *z, int n); /* ** Interfaces to register the language extensions. */ int th_register_language(Th_Interp *interp); /* th_lang.c */ int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */ int th_register_vfs(Th_Interp *interp); /* th_vfs.c */ int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */ #ifdef FOSSIL_ENABLE_TCL /* ** Interfaces to the full Tcl core library from "th_tcl.c". */ int th_register_tcl(Th_Interp *, void *); int unloadTcl(Th_Interp *, void *); int evaluateTclWithEvents(Th_Interp *, void *, const char *, int, int, int); #endif /* ** General purpose hash table from th_lang.c. */ typedef struct Th_Hash Th_Hash; typedef struct Th_HashEntry Th_HashEntry; struct Th_HashEntry { void *pData; char *zKey; int nKey; Th_HashEntry *pNext; /* Internal use only */ }; Th_Hash *Th_HashNew(Th_Interp *); void Th_HashDelete(Th_Interp *, Th_Hash *); void Th_HashIterate(Th_Interp*,Th_Hash*,int (*x)(Th_HashEntry*, void*),void*); Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int); /* ** Useful functions from th_lang.c. */ int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg); typedef struct Th_SubCommand {const char *zName; Th_CommandProc xProc;} Th_SubCommand; int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,const Th_SubCommand*); |
Changes to src/th_lang.c.
1 2 | /* | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | /* ** This file contains the implementation of all of the TH language ** built-in commands. ** ** All built-in commands are implemented using the public interface ** declared in th.h, so this file serves as both a part of the language ** implementation and an example of how to extend the language with ** new commands. */ #include "config.h" #include "th.h" #include <string.h> #include <assert.h> int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){ Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1); return TH_ERROR; } /* ** Syntax: ** ** catch script ?varname? */ static int catch_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "catch script ?varname?"); } rc = Th_Eval(interp, 0, argv[1], -1); if( argc==3 ){ int nResult; const char *zResult = Th_GetResult(interp, &nResult); Th_SetVar(interp, argv[2], argl[2], zResult, nResult); } Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH Syntax: ** ** if expr1 body1 ?elseif expr2 body2? ? ?else? bodyN? */ static int if_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc = TH_OK; int iCond; /* Result of evaluating expression */ int i; |
︙ | ︙ | |||
92 93 94 95 96 97 98 | return rc; wrong_args: return Th_WrongNumArgs(interp, "if ..."); } /* | | | | | | | | | | | | | | | | | | | | | | | | | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | return rc; wrong_args: return Th_WrongNumArgs(interp, "if ..."); } /* ** TH Syntax: ** ** expr expr */ static int expr_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "expr expression"); } return Th_Expr(interp, argv[1], argl[1]); } /* ** Evaluate the th1 script (zBody, nBody) in the local stack frame. ** Return the result of the evaluation, except if the result ** is TH_CONTINUE, return TH_OK instead. */ static int eval_loopbody(Th_Interp *interp, const char *zBody, int nBody){ int rc = Th_Eval(interp, 0, zBody, nBody); if( rc==TH_CONTINUE ){ rc = TH_OK; } return rc; } /* ** TH Syntax: ** ** for init condition incr script */ static int for_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; int iCond; if( argc!=5 ){ return Th_WrongNumArgs(interp, "for init condition incr script"); } /* Evaluate the 'init' script */ rc = Th_Eval(interp, 0, argv[1], -1); while( rc==TH_OK && TH_OK==(rc = Th_Expr(interp, argv[2], -1)) && TH_OK==(rc = Th_ToInt(interp, Th_GetResult(interp, 0), -1, &iCond)) && iCond && TH_OK==(rc = eval_loopbody(interp, argv[4], argl[4])) ){ rc = Th_Eval(interp, 0, argv[3], -1); } if( rc==TH_BREAK ) rc = TH_OK; return rc; } /* ** TH Syntax: ** ** list ?arg1 ?arg2? ...? */ static int list_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ char *zList = 0; int nList = 0; int i; for(i=1; i<argc; i++){ Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); } Th_SetResult(interp, zList, nList); Th_Free(interp, zList); return TH_OK; } /* ** TH Syntax: ** ** lindex list index */ static int lindex_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iElem; int rc; char **azElem; int *anElem; |
︙ | ︙ | |||
225 226 227 228 229 230 231 | Th_Free(interp, azElem); } return rc; } /* | | | | | | | | | | | | | | | | | | | | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | Th_Free(interp, azElem); } return rc; } /* ** TH Syntax: ** ** llength list */ static int llength_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int nElem; int rc; if( argc!=2 ){ return Th_WrongNumArgs(interp, "llength list"); } rc = Th_SplitList(interp, argv[1], argl[1], 0, 0, &nElem); if( rc==TH_OK ){ Th_SetResultInt(interp, nElem); } return rc; } /* ** TH Syntax: ** ** set varname ?value? */ static int set_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "set varname ?value?"); } if( argc==3 ){ Th_SetVar(interp, argv[1], argl[1], argv[2], argl[2]); } return Th_GetVar(interp, argv[1], argl[1]); } /* ** When a new command is created using the built-in [proc] command, an ** instance of the following structure is allocated and populated. A ** pointer to the structure is passed as the context (second) argument ** to function proc_call1() when the new command is executed. */ typedef struct ProcDefn ProcDefn; struct ProcDefn { int nParam; /* Number of formal (non "args") parameters */ char **azParam; /* Parameter names */ int *anParam; /* Lengths of parameter names */ char **azDefault; /* Default values */ int *anDefault; /* Lengths of default values */ int hasArgs; /* True if there is an "args" parameter */ char *zProgram; /* Body of proc */ int nProgram; /* Number of bytes at zProgram */ char *zUsage; /* Usage message */ int nUsage; /* Number of bytes at zUsage */ }; /* This structure is used to temporarily store arguments passed to an ** invocation of a command created using [proc]. A pointer to an ** instance is passed as the second argument to the proc_call2() function. */ typedef struct ProcArgs ProcArgs; struct ProcArgs { int argc; const char **argv; int *argl; |
︙ | ︙ | |||
321 322 323 324 325 326 327 | int i; ProcDefn *p = (ProcDefn *)pContext1; ProcArgs *pArgs = (ProcArgs *)pContext2; /* Check if there are the right number of arguments. If there are ** not, generate a usage message for the command. */ | | | 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | int i; ProcDefn *p = (ProcDefn *)pContext1; ProcArgs *pArgs = (ProcArgs *)pContext2; /* Check if there are the right number of arguments. If there are ** not, generate a usage message for the command. */ if( (pArgs->argc>(p->nParam+1) && !p->hasArgs) || (pArgs->argc<=(p->nParam) && !p->azDefault[pArgs->argc-1]) ){ char *zUsage = 0; int nUsage = 0; Th_StringAppend(interp, &zUsage, &nUsage, pArgs->argv[0], pArgs->argl[0]); Th_StringAppend(interp, &zUsage, &nUsage, p->zUsage, p->nUsage); Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"", 1); |
︙ | ︙ | |||
372 373 374 375 376 377 378 | /* ** This function is the command callback registered for all commands ** created using the [proc] command. The second argument, pContext, ** is a pointer to the associated ProcDefn structure. */ static int proc_call1( Th_Interp *interp, | | | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | /* ** This function is the command callback registered for all commands ** created using the [proc] command. The second argument, pContext, ** is a pointer to the associated ProcDefn structure. */ static int proc_call1( Th_Interp *interp, void *pContext, int argc, const char **argv, int *argl ){ int rc; ProcDefn *p = (ProcDefn *)pContext; ProcArgs procargs; |
︙ | ︙ | |||
398 399 400 401 402 403 404 | if( rc==TH_RETURN ){ rc = TH_OK; } return rc; } /* | | | | | | | | | | | | | | | | 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 | if( rc==TH_RETURN ){ rc = TH_OK; } return rc; } /* ** This function is registered as the delete callback for all commands ** created using the built-in [proc] command. It is called automatically ** when a command created using [proc] is deleted. ** ** It frees the ProcDefn structure allocated when the command was created. */ static void proc_del(Th_Interp *interp, void *pContext){ ProcDefn *p = (ProcDefn *)pContext; Th_Free(interp, (void *)p->zUsage); Th_Free(interp, (void *)p); } /* ** TH Syntax: ** ** proc name arglist code */ static int proc_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int rc; const char *zName; ProcDefn *p; int nByte; int i; char *zSpace; char **azParam; int *anParam; int nParam; char *zUsage = 0; /* Build up a usage message here */ int nUsage = 0; /* Number of bytes at zUsage */ if( argc!=4 ){ return Th_WrongNumArgs(interp, "proc name arglist code"); } if( Th_SplitList(interp, argv[2], argl[2], &azParam, &anParam, &nParam) ){ return TH_ERROR; } /* Allocate the new ProcDefn structure. */ nByte = sizeof(ProcDefn) + /* ProcDefn structure */ (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */ (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */ argl[3] + /* zProgram */ argl[2]; /* Space for copies of parameter names and default values */ p = (ProcDefn *)Th_Malloc(interp, nByte); /* If the last parameter in the parameter list is "args", then set the ** ProcDefn.hasArgs flag. The "args" parameter does not require an ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays. */ if( anParam[nParam-1]==4 && 0==memcmp(azParam[nParam-1], "args", 4) ){ p->hasArgs = 1; nParam--; } p->nParam = nParam; p->azParam = (char **)&p[1]; p->anParam = (int *)&p->azParam[nParam]; p->azDefault = (char **)&p->anParam[nParam]; p->anDefault = (int *)&p->azDefault[nParam]; p->zProgram = (char *)&p->anDefault[nParam]; memcpy(p->zProgram, argv[3], argl[3]); p->nProgram = argl[3]; zSpace = &p->zProgram[p->nProgram]; for(i=0; i<nParam; i++){ char **az; int *an; int n; if( Th_SplitList(interp, azParam[i], anParam[i], &az, &an, &n) ){ goto error_out; } |
︙ | ︙ | |||
519 520 521 522 523 524 525 | if( p->hasArgs ){ Th_StringAppend(interp, &zUsage, &nUsage, (const char *)" ?args...?", -1); } p->zUsage = zUsage; p->nUsage = nUsage; /* Register the new command with the th1 interpreter. */ | | | | | | | | | | | | | | | | | 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 | if( p->hasArgs ){ Th_StringAppend(interp, &zUsage, &nUsage, (const char *)" ?args...?", -1); } p->zUsage = zUsage; p->nUsage = nUsage; /* Register the new command with the th1 interpreter. */ zName = argv[1]; rc = Th_CreateCommand(interp, zName, proc_call1, (void *)p, proc_del); if( rc==TH_OK ){ Th_SetResult(interp, 0, 0); } Th_Free(interp, azParam); return TH_OK; error_out: Th_Free(interp, azParam); Th_Free(interp, zUsage); return TH_ERROR; } /* ** TH Syntax: ** ** rename oldcmd newcmd */ static int rename_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=3 ){ return Th_WrongNumArgs(interp, "rename oldcmd newcmd"); } return Th_RenameCommand(interp, argv[1], argl[1], argv[2], argl[2]); } /* ** TH Syntax: ** ** break ?value...? ** continue ?value...? ** ok ?value...? ** error ?value...? */ static int simple_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=1 && argc!=2 ){ return Th_WrongNumArgs(interp, "return ?value?"); } if( argc==2 ){ Th_SetResult(interp, argv[1], argl[1]); } return FOSSIL_PTR_TO_INT(ctx); } /* ** TH Syntax: ** ** return ?-code code? ?value? */ static int return_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iCode = TH_RETURN; if( argc<1 || argc>4 ){ return Th_WrongNumArgs(interp, "return ?-code code? ?value?"); } if( argc>2 ){ |
︙ | ︙ | |||
636 637 638 639 640 641 642 | } if( iRes==0 ){ iRes = nLeft-nRight; } if( iRes<0 ) iRes = -1; if( iRes>0 ) iRes = 1; | | < < < < | > > > | > | | | | | | | > | 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 | } if( iRes==0 ){ iRes = nLeft-nRight; } if( iRes<0 ) iRes = -1; if( iRes>0 ) iRes = 1; return Th_SetResultInt(interp, iRes); } /* ** TH Syntax: ** ** string first NEEDLE HAYSTACK */ static int string_first_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int nNeedle; int nHaystack; int iRes = -1; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string first needle haystack"); } nNeedle = argl[2]; nHaystack = argl[3]; if( nNeedle && nHaystack && nNeedle<=nHaystack ){ const char *zNeedle = argv[2]; const char *zHaystack = argv[3]; int i; for(i=0; i<=(nHaystack-nNeedle); i++){ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){ iRes = i; break; } } } return Th_SetResultInt(interp, iRes); } /* ** TH Syntax: ** ** string is CLASS STRING |
︙ | ︙ | |||
709 710 711 712 713 714 715 | ** TH Syntax: ** ** string last NEEDLE HAYSTACK */ static int string_last_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ | < < < | < | > > > | > | | | | | | | > | 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 | ** TH Syntax: ** ** string last NEEDLE HAYSTACK */ static int string_last_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int nNeedle; int nHaystack; int iRes = -1; if( argc!=4 ){ return Th_WrongNumArgs(interp, "string last needle haystack"); } nNeedle = argl[2]; nHaystack = argl[3]; if( nNeedle && nHaystack && nNeedle<=nHaystack ){ const char *zNeedle = argv[2]; const char *zHaystack = argv[3]; int i; for(i=nHaystack-nNeedle; i>=0; i--){ if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){ iRes = i; break; } } } return Th_SetResultInt(interp, iRes); } /* ** TH Syntax: ** ** string length STRING |
︙ | ︙ | |||
865 866 867 868 869 870 871 | /* ** TH Syntax: ** ** unset VAR */ static int unset_command( | | | | | | 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 | /* ** TH Syntax: ** ** unset VAR */ static int unset_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "unset var"); } return Th_UnsetVar(interp, argv[1], argl[1]); } int Th_CallSubCommand( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl, const Th_SubCommand *aSub ){ if( argc>1 ){ int i; for(i=0; aSub[i].zName; i++){ const char *zName = aSub[i].zName; if( th_strlen(zName)==argl[1] && 0==memcmp(zName, argv[1], argl[1]) ){ return aSub[i].xProc(interp, ctx, argc, argv, argl); } } } if(argc<2){ Th_ErrorMessage(interp, "Expected sub-command for", argv[0], argl[0]); |
︙ | ︙ | |||
914 915 916 917 918 919 920 | ** string is CLASS STRING ** string last NEEDLE HAYSTACK ?STARTINDEX? ** string length STRING ** string range STRING FIRST LAST ** string repeat STRING COUNT */ static int string_command( | | | | 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 | ** string is CLASS STRING ** string last NEEDLE HAYSTACK ?STARTINDEX? ** string length STRING ** string range STRING FIRST LAST ** string repeat STRING COUNT */ static int string_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ static const Th_SubCommand aSub[] = { { "compare", string_compare_command }, { "first", string_first_command }, { "is", string_is_command }, { "last", string_last_command }, { "length", string_length_command }, { "range", string_range_command }, { "repeat", string_repeat_command }, |
︙ | ︙ | |||
942 943 944 945 946 947 948 | /* ** TH Syntax: ** ** info exists VARNAME */ static int info_command( | | | | | | | | | 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 | /* ** TH Syntax: ** ** info exists VARNAME */ static int info_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ static const Th_SubCommand aSub[] = { { "exists", info_exists_command }, { 0, 0 } }; return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub); } /* ** Convert the script level frame specification (used by the commands ** [uplevel] and [upvar]) in (zFrame, nFrame) to an integer frame as ** used by Th_LinkVar() and Th_Eval(). If successful, write the integer ** frame level to *piFrame and return TH_OK. Otherwise, return TH_ERROR ** and leave an error message in the interpreter result. */ static int thToFrame( Th_Interp *interp, const char *zFrame, int nFrame, int *piFrame ){ int iFrame; if( th_isdigit(zFrame[0]) ){ int rc = Th_ToInt(interp, zFrame, nFrame, &iFrame); if( rc!=TH_OK ) return rc; iFrame = iFrame * -1; |
︙ | ︙ | |||
990 991 992 993 994 995 996 | /* ** TH Syntax: ** ** uplevel ?LEVEL? SCRIPT */ static int uplevel_command( | | | | | | | | | | | | | | 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 | /* ** TH Syntax: ** ** uplevel ?LEVEL? SCRIPT */ static int uplevel_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iFrame = -1; if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "uplevel ?level? script..."); } if( argc==3 && TH_OK!=thToFrame(interp, argv[1], argl[1], &iFrame) ){ return TH_ERROR; } return Th_Eval(interp, iFrame, argv[argc-1], -1); } /* ** TH Syntax: ** ** upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR ...? */ static int upvar_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int iVar = 1; int iFrame = -1; int rc = TH_OK; int i; if( TH_OK==thToFrame(0, argv[1], argl[1], &iFrame) ){ iVar++; } if( argc==iVar || (argc-iVar)%2 ){ return Th_WrongNumArgs(interp, "upvar frame othervar myvar ?othervar myvar...?"); } for(i=iVar; rc==TH_OK && i<argc; i=i+2){ rc = Th_LinkVar(interp, argv[i+1], argl[i+1], iFrame, argv[i], argl[i]); } return rc; } /* ** TH Syntax: ** ** breakpoint ARGS ** ** This command does nothing at all. Its purpose in life is to serve ** as a point for setting breakpoints in a debugger. */ static int breakpoint_command( Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl ){ int cnt = 0; cnt++; return TH_OK; } |
︙ | ︙ | |||
1076 1077 1078 1079 1080 1081 1082 | {"expr", expr_command, 0}, {"for", for_command, 0}, {"if", if_command, 0}, {"info", info_command, 0}, {"lindex", lindex_command, 0}, {"list", list_command, 0}, {"llength", llength_command, 0}, | | | | | | | 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 | {"expr", expr_command, 0}, {"for", for_command, 0}, {"if", if_command, 0}, {"info", info_command, 0}, {"lindex", lindex_command, 0}, {"list", list_command, 0}, {"llength", llength_command, 0}, {"proc", proc_command, 0}, {"rename", rename_command, 0}, {"set", set_command, 0}, {"string", string_command, 0}, {"unset", unset_command, 0}, {"uplevel", uplevel_command, 0}, {"upvar", upvar_command, 0}, {"breakpoint", breakpoint_command, 0}, {"return", return_command, 0}, {"break", simple_command, (void *)TH_BREAK}, {"continue", simple_command, (void *)TH_CONTINUE}, {"error", simple_command, (void *)TH_ERROR}, {0, 0, 0} }; size_t i; /* Add the language commands. */ for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){ void *ctx; if ( !aCommand[i].zName || !aCommand[i].xProc ) continue; ctx = aCommand[i].pContext; Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0); } return TH_OK; } |
Changes to src/th_main.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 | /* ** Flag parameters to the Th_FossilInit() routine used to control the ** interpreter creation and initialization process. */ #define TH_INIT_NONE ((u32)0x00000000) /* No flags. */ #define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */ #define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */ | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | /* ** Flag parameters to the Th_FossilInit() routine used to control the ** interpreter creation and initialization process. */ #define TH_INIT_NONE ((u32)0x00000000) /* No flags. */ #define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */ #define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */ #define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */ #define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */ #define TH_INIT_MASK ((u32)0x0000000F) /* All possible init flags. */ /* ** Useful and/or "well-known" combinations of flag values. */ #define TH_INIT_DEFAULT (TH_INIT_NONE) /* Default flags. */ #define TH_INIT_HOOK (TH_INIT_NEED_CONFIG | TH_INIT_FORCE_SETUP) #define TH_INIT_FORBID_MASK (TH_INIT_FORCE_TCL) /* Illegal from a script. */ #endif /* ** Flags set by functions in this file to keep track of integration state ** information. These flags should not be used outside of this file. */ #define TH_STATE_CONFIG ((u32)0x00000010) /* We opened the config. */ #define TH_STATE_REPOSITORY ((u32)0x00000020) /* We opened the repository. */ #define TH_STATE_MASK ((u32)0x00000030) /* All possible state flags. */ #ifdef FOSSIL_ENABLE_TH1_HOOKS /* ** These are the "well-known" TH1 error messages that occur when no hook is ** registered to be called prior to executing a command or processing a web ** page, respectively. If one of these errors is seen, it will not be sent ** or displayed to the remote user or local interactive user, respectively. */ #define NO_COMMAND_HOOK_ERROR "no such command: command_hook" #define NO_WEBPAGE_HOOK_ERROR "no such command: webpage_hook" #endif /* ** These macros are used within this file to detect if the repository and ** configuration ("user") database are currently open. */ #define Th_IsRepositoryOpen() (g.repositoryOpen) #define Th_IsConfigOpen() (g.zConfigDbName!=0) /* ** Global variable counting the number of outstanding calls to malloc() ** made by the th1 implementation. This is used to catch memory leaks ** in the interpreter. Obviously, it also means th1 is not threadsafe. */ static int nOutstandingMalloc = 0; |
︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | static void xFree(void *p){ if( p ){ nOutstandingMalloc--; } free(p); } static Th_Vtab vtab = { xMalloc, xFree }; /* ** Generate a TH1 trace message if debugging is enabled. */ void Th_Trace(const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); blob_vappendf(&g.thLog, zFormat, ap); va_end(ap); } /* ** Checks if the TH1 trace log needs to be enabled. If so, prepares ** it for use. */ void Th_InitTraceLog(){ g.thTrace = find_option("th-trace", 0, 0)!=0; | > > > > > > > > > > > > > > > > > > > | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | static void xFree(void *p){ if( p ){ nOutstandingMalloc--; } free(p); } static Th_Vtab vtab = { xMalloc, xFree }; /* ** Returns the number of outstanding TH1 memory allocations. */ int Th_GetOutstandingMalloc(){ return nOutstandingMalloc; } /* ** Generate a TH1 trace message if debugging is enabled. */ void Th_Trace(const char *zFormat, ...){ va_list ap; va_start(ap, zFormat); blob_vappendf(&g.thLog, zFormat, ap); va_end(ap); } /* ** Forces input and output to be done via the CGI subsystem. */ void Th_ForceCgi(int fullHttpReply){ g.httpOut = stdout; g.httpIn = stdin; fossil_binary_mode(g.httpOut); fossil_binary_mode(g.httpIn); g.cgiOutput = 1; g.fullHttpReply = fullHttpReply; } /* ** Checks if the TH1 trace log needs to be enabled. If so, prepares ** it for use. */ void Th_InitTraceLog(){ g.thTrace = find_option("th-trace", 0, 0)!=0; |
︙ | ︙ | |||
88 89 90 91 92 93 94 95 96 97 98 99 100 101 | void Th_PrintTraceLog(){ if( g.thTrace ){ fossil_print("\n------------------ BEGIN TRACE LOG ------------------\n"); fossil_print("%s", blob_str(&g.thLog)); fossil_print("\n------------------- END TRACE LOG -------------------\n"); } } /* ** True if output is enabled. False if disabled. */ static int enableOutput = 1; /* | > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | void Th_PrintTraceLog(){ if( g.thTrace ){ fossil_print("\n------------------ BEGIN TRACE LOG ------------------\n"); fossil_print("%s", blob_str(&g.thLog)); fossil_print("\n------------------- END TRACE LOG -------------------\n"); } } /* ** TH1 command: httpize STRING ** ** Escape all characters of STRING which have special meaning in URI ** components. Return a new string result. */ static int httpizeCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ char *zOut; if( argc!=2 ){ return Th_WrongNumArgs(interp, "httpize STRING"); } zOut = httpize((char*)argv[1], argl[1]); Th_SetResult(interp, zOut, -1); free(zOut); return TH_OK; } /* ** True if output is enabled. False if disabled. */ static int enableOutput = 1; /* ** TH1 command: enable_output BOOLEAN ** ** Enable or disable the puts and hputs commands. */ static int enableOutputCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int rc; if( argc<2 || argc>3 ){ return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN"); } rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput); if( g.thTrace ){ Th_Trace("enable_output {%.*s} -> %d<br>\n", argl[1],argv[1],enableOutput); } return rc; } /* ** Returns a name for a TH1 return code. */ const char *Th_ReturnCodeName(int rc, int nullIfOk){ static char zRc[32]; switch( rc ){ case TH_OK: return nullIfOk ? 0 : "TH_OK"; case TH_ERROR: return "TH_ERROR"; case TH_BREAK: return "TH_BREAK"; case TH_RETURN: return "TH_RETURN"; case TH_CONTINUE: return "TH_CONTINUE"; default: { sqlite3_snprintf(sizeof(zRc), zRc, "TH1 return code %d", rc); } } return zRc; } /* ** Send text to the appropriate output: Either to the console |
︙ | ︙ | |||
170 171 172 173 174 175 176 | sendText("ERROR: ", -1, 0); sendText((char*)z, n, 1); sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0); enableOutput = savedEnable; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | | | | | > > > > > > > > > > | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 | sendText("ERROR: ", -1, 0); sendText((char*)z, n, 1); sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0); enableOutput = savedEnable; } /* ** Convert name to an rid. This function was copied from name_to_typed_rid() ** in name.c; however, it has been modified to report TH1 script errors instead ** of "fatal errors". */ int th1_name_to_typed_rid( Th_Interp *interp, const char *zName, const char *zType ){ int rid; if( zName==0 || zName[0]==0 ) return 0; rid = symbolic_name_to_rid(zName, zType); if( rid<0 ){ Th_SetResult(interp, "ambiguous name", -1); }else if( rid==0 ){ Th_SetResult(interp, "name not found", -1); } return rid; } /* ** Attempt to lookup the specified check-in and file name into an rid. ** This function was copied from artifact_from_ci_and_filename() in ** info.c; however, it has been modified to report TH1 script errors ** instead of "fatal errors". */ int th1_artifact_from_ci_and_filename( Th_Interp *interp, const char *zCI, const char *zFilename ){ int cirid; Blob err; Manifest *pManifest; ManifestFile *pFile; if( zCI==0 ){ Th_SetResult(interp, "invalid check-in", -1); return 0; } if( zFilename==0 ){ Th_SetResult(interp, "invalid file name", -1); return 0; } cirid = th1_name_to_typed_rid(interp, zCI, "*"); blob_zero(&err); pManifest = manifest_get(cirid, CFTYPE_MANIFEST, &err); if( pManifest==0 ){ if( blob_size(&err)>0 ){ Th_SetResult(interp, blob_str(&err), blob_size(&err)); }else{ Th_SetResult(interp, "manifest not found", -1); } blob_reset(&err); return 0; } blob_reset(&err); manifest_file_rewind(pManifest); while( (pFile = manifest_file_next(pManifest,0))!=0 ){ if( fossil_strcmp(zFilename, pFile->zName)==0 ){ int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid); manifest_destroy(pManifest); return rid; } } Th_SetResult(interp, "file name not found in manifest", -1); return 0; } /* ** TH1 command: puts STRING ** TH1 command: html STRING ** ** Output STRING escaped for HTML (html) or unchanged (puts). */ static int putsCmd( Th_Interp *interp, void *pConvert, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "puts STRING"); } sendText((char*)argv[1], argl[1], *(unsigned int*)pConvert); return TH_OK; } /* ** TH1 command: wiki STRING ** ** Render the input string as wiki. */ static int wikiCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int flags = WIKI_INLINE | WIKI_NOBADLINKS | *(unsigned int*)p; if( argc!=2 ){ return Th_WrongNumArgs(interp, "wiki STRING"); } if( enableOutput ){ Blob src; blob_init(&src, (char*)argv[1], argl[1]); wiki_convert(&src, 0, flags); blob_reset(&src); } return TH_OK; } /* ** TH1 command: htmlize STRING ** ** Escape all characters of STRING which have special meaning in HTML. ** Return a new string result. */ static int htmlizeCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ char *zOut; if( argc!=2 ){ return Th_WrongNumArgs(interp, "htmlize STRING"); } zOut = htmlize((char*)argv[1], argl[1]); Th_SetResult(interp, zOut, -1); free(zOut); return TH_OK; } /* ** TH1 command: date ** ** Return a string which is the current time and date. If the ** -local option is used, the date appears using localtime instead ** of UTC. */ static int dateCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ char *zOut; if( argc>=2 && argl[1]==6 && memcmp(argv[1],"-local",6)==0 ){ zOut = db_text("??", "SELECT datetime('now'%s)", timeline_utc()); }else{ zOut = db_text("??", "SELECT datetime('now')"); } Th_SetResult(interp, zOut, -1); free(zOut); return TH_OK; } /* ** TH1 command: hascap STRING... ** TH1 command: anoncap STRING... ** ** Return true if the current user (hascap) or if the anonymous user ** (anoncap) has all of the capabilities listed in STRING. */ static int hascapCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int rc = 0, i; if( argc<2 ){ return Th_WrongNumArgs(interp, "hascap STRING ..."); } for(i=1; i<argc && rc==0; i++){ rc = login_has_capability((char*)argv[i],argl[i],*(int*)p); } if( g.thTrace ){ Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc); } Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH1 command: searchable STRING... ** ** Return true if searching in any of the document classes identified ** by STRING is enabled for the repository and user has the necessary ** capabilities to perform the search. ** ** Document classes: ** ** c Check-in comments ** d Embedded documentation ** t Tickets ** w Wiki ** ** To be clear, only one of the document classes identified by each STRING ** needs to be searchable in order for that argument to be true. But ** all arguments must be true for this routine to return true. Hence, to ** see if ALL document classes are searchable: ** ** if {[searchable c d t w]} {...} ** ** But to see if ANY document class is searchable: ** ** if {[searchable cdtw]} {...} ** ** This command is useful for enabling or disabling a "Search" entry ** on the menu bar. */ static int searchableCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int rc = 1, i, j; unsigned int searchCap = search_restrict(SRCH_ALL); if( argc<2 ){ return Th_WrongNumArgs(interp, "hascap STRING ..."); } for(i=1; i<argc && rc; i++){ int match = 0; for(j=0; j<argl[i]; j++){ switch( argv[i][j] ){ case 'c': match |= searchCap & SRCH_CKIN; break; case 'd': match |= searchCap & SRCH_DOC; break; case 't': match |= searchCap & SRCH_TKT; break; case 'w': match |= searchCap & SRCH_WIKI; break; } } if( !match ) rc = 0; } if( g.thTrace ){ Th_Trace("[searchable %#h] => %d<br />\n", argl[1], argv[1], rc); } Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH1 command: hasfeature STRING ** ** Return true if the fossil binary has the given compile-time feature ** enabled. The set of features includes: ** ** "ssl" = FOSSIL_ENABLE_SSL ** "th1Docs" = FOSSIL_ENABLE_TH1_DOCS ** "th1Hooks" = FOSSIL_ENABLE_TH1_HOOKS ** "tcl" = FOSSIL_ENABLE_TCL ** "useTclStubs" = USE_TCL_STUBS ** "tclStubs" = FOSSIL_ENABLE_TCL_STUBS ** "tclPrivateStubs" = FOSSIL_ENABLE_TCL_PRIVATE_STUBS ** "json" = FOSSIL_ENABLE_JSON ** "markdown" = FOSSIL_ENABLE_MARKDOWN ** "unicodeCmdLine" = !BROKEN_MINGW_CMDLINE ** */ static int hasfeatureCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int rc = 0; const char *zArg; if( argc!=2 ){ return Th_WrongNumArgs(interp, "hasfeature STRING"); } zArg = (const char *)argv[1]; if(NULL==zArg){ /* placeholder for following ifdefs... */ } #if defined(FOSSIL_ENABLE_SSL) else if( 0 == fossil_strnicmp( zArg, "ssl\0", 4 ) ){ rc = 1; } #endif #if defined(FOSSIL_ENABLE_TH1_DOCS) else if( 0 == fossil_strnicmp( zArg, "th1Docs\0", 8 ) ){ rc = 1; } #endif #if defined(FOSSIL_ENABLE_TH1_HOOKS) else if( 0 == fossil_strnicmp( zArg, "th1Hooks\0", 9 ) ){ rc = 1; } #endif #if defined(FOSSIL_ENABLE_TCL) else if( 0 == fossil_strnicmp( zArg, "tcl\0", 4 ) ){ rc = 1; } #endif #if defined(USE_TCL_STUBS) |
︙ | ︙ | |||
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | rc = 1; } #endif #if defined(FOSSIL_ENABLE_JSON) else if( 0 == fossil_strnicmp( zArg, "json\0", 5 ) ){ rc = 1; } #endif else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){ rc = 1; } if( g.thTrace ){ Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc); } Th_SetResultInt(interp, rc); return TH_OK; } /* | > > > > > | | 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 | rc = 1; } #endif #if defined(FOSSIL_ENABLE_JSON) else if( 0 == fossil_strnicmp( zArg, "json\0", 5 ) ){ rc = 1; } #endif #if !defined(BROKEN_MINGW_CMDLINE) else if( 0 == fossil_strnicmp( zArg, "unicodeCmdLine\0", 15 ) ){ rc = 1; } #endif else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){ rc = 1; } if( g.thTrace ){ Th_Trace("[hasfeature %#h] => %d<br />\n", argl[1], zArg, rc); } Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH1 command: tclReady ** ** Return true if the fossil binary has the Tcl integration feature ** enabled and it is currently available for use by TH1 scripts. ** */ static int tclReadyCmd( Th_Interp *interp, |
︙ | ︙ | |||
392 393 394 395 396 397 398 | } Th_SetResultInt(interp, rc); return TH_OK; } /* | | > | | | | | | | | | | | 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 | } Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH1 command: anycap STRING ** ** Return true if the current user user ** has any one of the capabilities listed in STRING. */ static int anycapCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int rc = 0; int i; if( argc!=2 ){ return Th_WrongNumArgs(interp, "anycap STRING"); } for(i=0; rc==0 && i<argl[1]; i++){ rc = login_has_capability((char*)&argv[1][i],1,0); } if( g.thTrace ){ Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc); } Th_SetResultInt(interp, rc); return TH_OK; } /* ** TH1 command: combobox NAME TEXT-LIST NUMLINES ** ** Generate an HTML combobox. NAME is both the name of the ** CGI parameter and the name of a variable that contains the ** currently selected value. TEXT-LIST is a list of possible ** values for the combobox. NUMLINES is 1 for a true combobox. ** If NUMLINES is greater than one then the display is a listbox ** with the number of lines given. */ static int comboboxCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=4 ){ return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES"); } if( enableOutput ){ int height; |
︙ | ︙ | |||
461 462 463 464 465 466 467 | z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height); free(zH); sendText(z, -1, 0); free(z); blob_reset(&name); for(i=0; i<nElem; i++){ zH = htmlize((char*)azElem[i], aszElem[i]); | | | | | | | 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 | z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height); free(zH); sendText(z, -1, 0); free(z); blob_reset(&name); for(i=0; i<nElem; i++){ zH = htmlize((char*)azElem[i], aszElem[i]); if( zValue && aszElem[i]==nValue && memcmp(zValue, azElem[i], nValue)==0 ){ z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>", zH, zH); }else{ z = mprintf("<option value=\"%s\">%s</option>", zH, zH); } free(zH); sendText(z, -1, 0); free(z); } sendText("</select>", -1, 0); Th_Free(interp, azElem); } return TH_OK; } /* ** TH1 command: linecount STRING MAX MIN ** ** Return one more than the number of \n characters in STRING. But ** never return less than MIN or more than MAX. */ static int linecntCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ const char *z; int size, n, i; int iMin, iMax; if( argc!=4 ){ return Th_WrongNumArgs(interp, "linecount STRING MAX MIN"); |
︙ | ︙ | |||
514 515 516 517 518 519 520 | if( n<iMin ) n = iMin; if( n>iMax ) n = iMax; Th_SetResultInt(interp, n); return TH_OK; } /* | | | | | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 | if( n<iMin ) n = iMin; if( n>iMax ) n = iMax; Th_SetResultInt(interp, n); return TH_OK; } /* ** TH1 command: repository ?BOOLEAN? ** ** Return the fully qualified file name of the open repository or an empty ** string if one is not currently open. Optionally, it will attempt to open ** the repository if the boolean argument is non-zero. */ static int repositoryCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=1 && argc!=2 ){ return Th_WrongNumArgs(interp, "repository ?BOOLEAN?"); } if( argc==2 ){ int openRepository = 0; if( Th_ToInt(interp, argv[1], argl[1], &openRepository) ){ return TH_ERROR; } if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0); } Th_SetResult(interp, g.zRepositoryName, -1); return TH_OK; } /* ** TH1 command: checkout ?BOOLEAN? ** ** Return the fully qualified directory name of the current checkout or an ** empty string if it is not available. Optionally, it will attempt to find ** the current checkout, opening the configuration ("user") database and the ** repository as necessary, if the boolean argument is non-zero. */ static int checkoutCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=1 && argc!=2 ){ return Th_WrongNumArgs(interp, "checkout ?BOOLEAN?"); } if( argc==2 ){ int openCheckout = 0; if( Th_ToInt(interp, argv[1], argl[1], &openCheckout) ){ return TH_ERROR; } if( openCheckout ) db_open_local(0); } Th_SetResult(interp, g.zLocalRoot, -1); return TH_OK; } /* ** TH1 command: trace STRING ** ** Generate a TH1 trace message if debugging is enabled. */ static int traceCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "trace STRING"); } if( g.thTrace ){ Th_Trace("%s", argv[1]); } Th_SetResult(interp, 0, 0); return TH_OK; } /* ** TH1 command: globalState NAME ?DEFAULT? ** ** Returns a string containing the value of the specified global state ** variable -OR- the specified default value. Currently, the supported ** items are: ** ** "checkout" = The active local checkout directory, if any. ** "configuration" = The active configuration database file name, ** if any. ** "executable" = The fully qualified executable file name. ** "flags" = The TH1 initialization flags. ** "log" = The error log file name, if any. ** "repository" = The active local repository file name, if ** any. ** "top" = The base path for the active server instance, ** if applicable. ** "user" = The active user name, if any. ** "vfs" = The SQLite VFS in use, if overridden. ** ** Attempts to query for unsupported global state variables will result ** in a script error. Additional global state variables may be exposed ** in the future. ** ** See also: checkout, repository, setting */ static int globalStateCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ const char *zDefault = 0; if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "globalState NAME ?DEFAULT?"); } if( argc==3 ){ zDefault = argv[2]; } if( fossil_strnicmp(argv[1], "checkout\0", 9)==0 ){ Th_SetResult(interp, g.zLocalRoot ? g.zLocalRoot : zDefault, -1); return TH_OK; }else if( fossil_strnicmp(argv[1], "configuration\0", 14)==0 ){ Th_SetResult(interp, g.zConfigDbName ? g.zConfigDbName : zDefault, -1); return TH_OK; }else if( fossil_strnicmp(argv[1], "executable\0", 11)==0 ){ Th_SetResult(interp, g.nameOfExe ? g.nameOfExe : zDefault, -1); return TH_OK; }else if( fossil_strnicmp(argv[1], "flags\0", 6)==0 ){ Th_SetResultInt(interp, g.th1Flags); return TH_OK; }else if( fossil_strnicmp(argv[1], "log\0", 4)==0 ){ Th_SetResult(interp, g.zErrlog ? g.zErrlog : zDefault, -1); return TH_OK; }else if( fossil_strnicmp(argv[1], "repository\0", 11)==0 ){ Th_SetResult(interp, g.zRepositoryName ? g.zRepositoryName : zDefault, -1); return TH_OK; }else if( fossil_strnicmp(argv[1], "top\0", 4)==0 ){ Th_SetResult(interp, g.zTop ? g.zTop : zDefault, -1); return TH_OK; }else if( fossil_strnicmp(argv[1], "user\0", 5)==0 ){ Th_SetResult(interp, g.zLogin ? g.zLogin : zDefault, -1); return TH_OK; }else if( fossil_strnicmp(argv[1], "vfs\0", 4)==0 ){ Th_SetResult(interp, g.zVfsName ? g.zVfsName : zDefault, -1); return TH_OK; }else{ Th_ErrorMessage(interp, "unsupported global state:", argv[1], argl[1]); return TH_ERROR; } } /* ** TH1 command: getParameter NAME ?DEFAULT? ** ** Return the value of the specified query parameter or the specified default ** value when there is no matching query parameter. */ static int getParameterCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ const char *zDefault = 0; if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "getParameter NAME ?DEFAULT?"); } if( argc==3 ){ zDefault = argv[2]; } Th_SetResult(interp, cgi_parameter(argv[1], zDefault), -1); return TH_OK; } /* ** TH1 command: setParameter NAME VALUE ** ** Sets the value of the specified query parameter. */ static int setParameterCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=3 ){ return Th_WrongNumArgs(interp, "setParameter NAME VALUE"); } cgi_replace_parameter(mprintf("%s", argv[1]), mprintf("%s", argv[2])); return TH_OK; } /* ** TH1 command: reinitialize ?FLAGS? ** ** Reinitializes the TH1 interpreter using the specified flags. */ static int reinitializeCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ u32 flags = TH_INIT_DEFAULT; if( argc!=1 && argc!=2 ){ return Th_WrongNumArgs(interp, "reinitialize ?FLAGS?"); } if( argc==2 ){ int iFlags; if( Th_ToInt(interp, argv[1], argl[1], &iFlags) ){ return TH_ERROR; }else{ flags = (u32)iFlags; } } Th_FossilInit(flags & ~TH_INIT_FORBID_MASK); Th_SetResult(interp, 0, 0); return TH_OK; } /* ** TH1 command: render STRING ** ** Renders the template and writes the results. */ static int renderCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int rc; if( argc!=2 ){ return Th_WrongNumArgs(interp, "render STRING"); } rc = Th_Render(argv[1]); Th_SetResult(interp, 0, 0); return rc; } /* ** TH1 command: styleHeader TITLE ** ** Render the configured style header. */ static int styleHeaderCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=2 ){ return Th_WrongNumArgs(interp, "styleHeader TITLE"); } if( Th_IsRepositoryOpen() ){ style_header("%s", argv[1]); Th_SetResult(interp, 0, 0); return TH_OK; }else{ Th_SetResult(interp, "repository unavailable", -1); return TH_ERROR; } } /* ** TH1 command: styleFooter ** ** Render the configured style footer. */ static int styleFooterCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=1 ){ return Th_WrongNumArgs(interp, "styleFooter"); } if( Th_IsRepositoryOpen() ){ style_footer(); Th_SetResult(interp, 0, 0); return TH_OK; }else{ Th_SetResult(interp, "repository unavailable", -1); return TH_ERROR; } } /* ** TH1 command: artifact ID ?FILENAME? ** ** Attempts to locate the specified artifact and return its contents. An ** error is generated if the repository is not open or the artifact cannot ** be found. */ static int artifactCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ if( argc!=2 && argc!=3 ){ return Th_WrongNumArgs(interp, "artifact ID ?FILENAME?"); } if( Th_IsRepositoryOpen() ){ int rid; Blob content; if( argc==3 ){ rid = th1_artifact_from_ci_and_filename(interp, argv[1], argv[2]); }else{ rid = th1_name_to_typed_rid(interp, argv[1], "*"); } if( rid!=0 && content_get(rid, &content) ){ Th_SetResult(interp, blob_str(&content), blob_size(&content)); blob_reset(&content); return TH_OK; }else{ return TH_ERROR; } }else{ Th_SetResult(interp, "repository unavailable", -1); return TH_ERROR; } } #ifdef _WIN32 # include <windows.h> #else # include <sys/time.h> # include <sys/resource.h> #endif |
︙ | ︙ | |||
574 575 576 577 578 579 580 | #else struct rusage s; getrusage(RUSAGE_SELF, &s); if( piUser ){ *piUser = ((sqlite3_uint64)s.ru_utime.tv_sec)*1000000 + s.ru_utime.tv_usec; } if( piKernel ){ | | | | | | | | | | | | | | | | 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 | #else struct rusage s; getrusage(RUSAGE_SELF, &s); if( piUser ){ *piUser = ((sqlite3_uint64)s.ru_utime.tv_sec)*1000000 + s.ru_utime.tv_usec; } if( piKernel ){ *piKernel = ((sqlite3_uint64)s.ru_stime.tv_sec)*1000000 + s.ru_stime.tv_usec; } #endif } /* ** TH1 command: utime ** ** Return the number of microseconds of CPU time consumed by the current ** process in user space. */ static int utimeCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ sqlite3_uint64 x; char zUTime[50]; getCpuTimes(&x, 0); sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x); Th_SetResult(interp, zUTime, -1); return TH_OK; } /* ** TH1 command: stime ** ** Return the number of microseconds of CPU time consumed by the current ** process in system space. */ static int stimeCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ sqlite3_uint64 x; char zUTime[50]; getCpuTimes(0, &x); sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x); Th_SetResult(interp, zUTime, -1); return TH_OK; } /* ** TH1 command: randhex N ** ** Return N*2 random hexadecimal digits with N<50. If N is omitted, ** use a value of 10. */ static int randhexCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int n; unsigned char aRand[50]; unsigned char zOut[100]; if( argc!=1 && argc!=2 ){ return Th_WrongNumArgs(interp, "repository ?BOOLEAN?"); |
︙ | ︙ | |||
658 659 660 661 662 663 664 | sqlite3_randomness(n, aRand); encode16(aRand, zOut, n); Th_SetResult(interp, (const char *)zOut, -1); return TH_OK; } /* | | | | | | 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 | sqlite3_randomness(n, aRand); encode16(aRand, zOut, n); Th_SetResult(interp, (const char *)zOut, -1); return TH_OK; } /* ** TH1 command: query SQL CODE ** ** Run the SQL query given by the SQL argument. For each row in the result ** set, run CODE. ** ** In SQL, parameters such as $var are filled in using the value of variable ** "var". Result values are stored in variables with the column name prior ** to each invocation of CODE. */ static int queryCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ sqlite3_stmt *pStmt; int rc; const char *zSql; int nSql; const char *zTail; |
︙ | ︙ | |||
735 736 737 738 739 740 741 | if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; } rc = sqlite3_finalize(pStmt); if( rc!=SQLITE_OK ){ Th_ErrorMessage(interp, "SQL error: ", sqlite3_errmsg(g.db), -1); return TH_ERROR; } | | | | 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 | if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK; } rc = sqlite3_finalize(pStmt); if( rc!=SQLITE_OK ){ Th_ErrorMessage(interp, "SQL error: ", sqlite3_errmsg(g.db), -1); return TH_ERROR; } } return res; } /* ** TH1 command: setting name ** ** Gets and returns the value of the specified Fossil setting. */ #define SETTING_WRONGNUMARGS "setting ?-strict? ?--? name" static int settingCmd( Th_Interp *interp, void *p, |
︙ | ︙ | |||
785 786 787 788 789 790 791 | Th_Trace("[setting %s%#h] => %d<br />\n", strict ? "strict " : "", argl[nArg], argv[nArg], rc); } return rc; } /* | | | 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 | Th_Trace("[setting %s%#h] => %d<br />\n", strict ? "strict " : "", argl[nArg], argv[nArg], rc); } return rc; } /* ** TH1 command: regexp ?-nocase? ?--? exp string ** ** Checks the string against the specified regular expression and returns ** non-zero if it matches. If the regular expression is invalid or cannot ** be compiled, an error will be generated. */ #define REGEXP_WRONGNUMARGS "regexp ?-nocase? ?--? exp string" static int regexpCmd( |
︙ | ︙ | |||
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 | }else{ Th_SetResult(interp, zErr, -1); rc = TH_ERROR; } re_free(pRe); return rc; } /* ** Make sure the interpreter has been initialized. Initialize it if ** it has not been already. ** ** The interpreter is stored in the g.interp global variable. */ void Th_FossilInit(u32 flags){ int wasInit = 0; int needConfig = flags & TH_INIT_NEED_CONFIG; int forceReset = flags & TH_INIT_FORCE_RESET; int forceTcl = flags & TH_INIT_FORCE_TCL; int forceSetup = flags & TH_INIT_FORCE_SETUP; static unsigned int aFlags[] = { 0, 1, WIKI_LINKSONLY }; static struct _Command { const char *zName; Th_CommandProc xProc; void *pContext; } aCommand[] = { {"anycap", anycapCmd, 0}, {"combobox", comboboxCmd, 0}, {"date", dateCmd, 0}, {"decorate", wikiCmd, (void*)&aFlags[2]}, {"enable_output", enableOutputCmd, 0}, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > < | > | | 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 | }else{ Th_SetResult(interp, zErr, -1); rc = TH_ERROR; } re_free(pRe); return rc; } /* ** TH1 command: http ?-asynchronous? ?--? url ?payload? ** ** Perform an HTTP or HTTPS request for the specified URL. If a ** payload is present, it will be interpreted as text/plain and ** the POST method will be used; otherwise, the GET method will ** be used. Upon success, if the -asynchronous option is used, an ** empty string is returned as the result; otherwise, the response ** from the server is returned as the result. Synchronous requests ** are not currently implemented. */ #define HTTP_WRONGNUMARGS "http ?-asynchronous? ?--? url ?payload?" static int httpCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int nArg = 1; int fAsynchronous = 0; const char *zType, *zRegexp; Blob payload; ReCompiled *pRe = 0; UrlData urlData; if( argc<2 || argc>5 ){ return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS); } if( fossil_strnicmp(argv[nArg], "-asynchronous", argl[nArg])==0 ){ fAsynchronous = 1; nArg++; } if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; if( nArg+1!=argc && nArg+2!=argc ){ return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); } memset(&urlData, '\0', sizeof(urlData)); url_parse_local(argv[nArg], 0, &urlData); if( urlData.isSsh || urlData.isFile ){ Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0); return TH_ERROR; } zRegexp = db_get("th1-uri-regexp", 0); if( zRegexp && zRegexp[0] ){ const char *zErr = re_compile(&pRe, zRegexp, 0); if( zErr ){ Th_SetResult(interp, zErr, -1); return TH_ERROR; } } if( !pRe || !re_match(pRe, (const unsigned char *)urlData.canonical, -1) ){ Th_SetResult(interp, "url not allowed", -1); re_free(pRe); return TH_ERROR; } re_free(pRe); blob_zero(&payload); if( nArg+2==argc ){ blob_append(&payload, argv[nArg+1], argl[nArg+1]); zType = "POST"; }else{ zType = "GET"; } if( fAsynchronous ){ const char *zSep, *zParams; Blob hdr; zParams = strrchr(argv[nArg], '?'); if( strlen(urlData.path)>0 && zParams!=argv[nArg] ){ zSep = ""; }else{ zSep = "/"; } blob_zero(&hdr); blob_appendf(&hdr, "%s %s%s%s HTTP/1.0\r\n", zType, zSep, urlData.path, zParams ? zParams : ""); if( urlData.proxyAuth ){ blob_appendf(&hdr, "Proxy-Authorization: %s\r\n", urlData.proxyAuth); } if( urlData.passwd && urlData.user && urlData.passwd[0]=='#' ){ char *zCredentials = mprintf("%s:%s", urlData.user, &urlData.passwd[1]); char *zEncoded = encode64(zCredentials, -1); blob_appendf(&hdr, "Authorization: Basic %s\r\n", zEncoded); fossil_free(zEncoded); fossil_free(zCredentials); } blob_appendf(&hdr, "Host: %s\r\n" "User-Agent: %s\r\n", urlData.hostname, get_user_agent()); if( zType[0]=='P' ){ blob_appendf(&hdr, "Content-Type: application/x-www-form-urlencoded\r\n" "Content-Length: %d\r\n\r\n", blob_size(&payload)); }else{ blob_appendf(&hdr, "\r\n"); } if( transport_open(&urlData) ){ Th_ErrorMessage(interp, transport_errmsg(&urlData), 0, 0); blob_reset(&hdr); blob_reset(&payload); return TH_ERROR; } transport_send(&urlData, &hdr); transport_send(&urlData, &payload); blob_reset(&hdr); blob_reset(&payload); transport_close(&urlData); Th_SetResult(interp, 0, 0); /* NOTE: Asynchronous, no results. */ return TH_OK; }else{ Th_ErrorMessage(interp, "synchronous requests are not yet implemented", 0, 0); blob_reset(&payload); return TH_ERROR; } } /* ** Attempts to open the configuration ("user") database. Optionally, also ** attempts to try to find the repository and open it. */ void Th_OpenConfig( int openRepository ){ if( openRepository && !Th_IsRepositoryOpen() ){ db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0); if( Th_IsRepositoryOpen() ){ g.th1Flags |= TH_STATE_REPOSITORY; }else{ g.th1Flags &= ~TH_STATE_REPOSITORY; } } if( !Th_IsConfigOpen() ){ db_open_config(0); if( Th_IsConfigOpen() ){ g.th1Flags |= TH_STATE_CONFIG; }else{ g.th1Flags &= ~TH_STATE_CONFIG; } } } /* ** Attempts to close the configuration ("user") database. Optionally, also ** attempts to close the repository. */ void Th_CloseConfig( int closeRepository ){ if( g.th1Flags & TH_STATE_CONFIG ){ db_close_config(); g.th1Flags &= ~TH_STATE_CONFIG; } if( closeRepository && (g.th1Flags & TH_STATE_REPOSITORY) ){ db_close(1); g.th1Flags &= ~TH_STATE_REPOSITORY; } } /* ** Make sure the interpreter has been initialized. Initialize it if ** it has not been already. ** ** The interpreter is stored in the g.interp global variable. */ void Th_FossilInit(u32 flags){ int wasInit = 0; int needConfig = flags & TH_INIT_NEED_CONFIG; int forceReset = flags & TH_INIT_FORCE_RESET; int forceTcl = flags & TH_INIT_FORCE_TCL; int forceSetup = flags & TH_INIT_FORCE_SETUP; static unsigned int aFlags[] = { 0, 1, WIKI_LINKSONLY }; static int anonFlag = LOGIN_ANON; static int zeroInt = 0; static struct _Command { const char *zName; Th_CommandProc xProc; void *pContext; } aCommand[] = { {"anoncap", hascapCmd, (void*)&anonFlag}, {"anycap", anycapCmd, 0}, {"artifact", artifactCmd, 0}, {"checkout", checkoutCmd, 0}, {"combobox", comboboxCmd, 0}, {"date", dateCmd, 0}, {"decorate", wikiCmd, (void*)&aFlags[2]}, {"enable_output", enableOutputCmd, 0}, {"getParameter", getParameterCmd, 0}, {"globalState", globalStateCmd, 0}, {"httpize", httpizeCmd, 0}, {"hascap", hascapCmd, (void*)&zeroInt}, {"hasfeature", hasfeatureCmd, 0}, {"html", putsCmd, (void*)&aFlags[0]}, {"htmlize", htmlizeCmd, 0}, {"http", httpCmd, 0}, {"linecount", linecntCmd, 0}, {"puts", putsCmd, (void*)&aFlags[1]}, {"query", queryCmd, 0}, {"randhex", randhexCmd, 0}, {"regexp", regexpCmd, 0}, {"reinitialize", reinitializeCmd, 0}, {"render", renderCmd, 0}, {"repository", repositoryCmd, 0}, {"searchable", searchableCmd, 0}, {"setParameter", setParameterCmd, 0}, {"setting", settingCmd, 0}, {"styleHeader", styleHeaderCmd, 0}, {"styleFooter", styleFooterCmd, 0}, {"tclReady", tclReadyCmd, 0}, {"trace", traceCmd, 0}, {"stime", stimeCmd, 0}, {"utime", utimeCmd, 0}, {"wiki", wikiCmd, (void*)&aFlags[0]}, {0, 0, 0} }; if( g.thTrace ){ Th_Trace("th1-init 0x%x => 0x%x<br />\n", g.th1Flags, flags); } if( needConfig ){ /* ** This function uses several settings which may be defined in the ** repository and/or the global configuration. Since the caller ** passed a non-zero value for the needConfig parameter, make sure ** the necessary database connections are open prior to continuing. */ Th_OpenConfig(1); } if( forceReset || forceTcl || g.interp==0 ){ int created = 0; int i; if( g.interp==0 ){ g.interp = Th_CreateInterp(&vtab); created = 1; } if( forceReset || created ){ th_register_language(g.interp); /* Basic scripting commands. */ } #ifdef FOSSIL_ENABLE_TCL if( forceTcl || fossil_getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){ if( !g.tcl.setup ){ g.tcl.setup = db_get("tcl-setup", 0); /* Grab Tcl setup script. */ } th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */ } #endif for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){ |
︙ | ︙ | |||
921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 | } } if( g.thTrace ){ Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup, Th_ReturnCodeName(rc, 0)); } } } /* ** Store a string value in a variable in the interpreter. */ void Th_Store(const char *zName, const char *zValue){ Th_FossilInit(TH_INIT_DEFAULT); if( zValue ){ if( g.thTrace ){ Th_Trace("set %h {%h}<br />\n", zName, zValue); } Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); } } /* ** Store an integer value in a variable in the interpreter. */ void Th_StoreInt(const char *zName, int iValue){ Blob value; char *zValue; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 | } } if( g.thTrace ){ Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup, Th_ReturnCodeName(rc, 0)); } } g.th1Flags &= ~TH_INIT_MASK; g.th1Flags |= (flags & TH_INIT_MASK); } /* ** Store a string value in a variable in the interpreter. */ void Th_Store(const char *zName, const char *zValue){ Th_FossilInit(TH_INIT_DEFAULT); if( zValue ){ if( g.thTrace ){ Th_Trace("set %h {%h}<br />\n", zName, zValue); } Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); } } /* ** Appends an element to a TH1 list value. This function is called by the ** transfer subsystem; therefore, it must be very careful to avoid doing ** any unnecessary work. To that end, the TH1 subsystem will not be called ** or initialized if the list pointer is zero (i.e. which will be the case ** when TH1 transfer hooks are disabled). */ void Th_AppendToList( char **pzList, int *pnList, const char *zElem, int nElem ){ if( pzList && zElem ){ Th_FossilInit(TH_INIT_DEFAULT); Th_ListAppend(g.interp, pzList, pnList, zElem, nElem); } } /* ** Stores a list value in the specified TH1 variable using the specified ** array of strings as the source of the element values. */ void Th_StoreList( const char *zName, char **pzList, int nList ){ Th_FossilInit(TH_INIT_DEFAULT); if( pzList ){ char *zValue = 0; int nValue = 0; int i; for(i=0; i<nList; i++){ Th_ListAppend(g.interp, &zValue, &nValue, pzList[i], -1); } if( g.thTrace ){ Th_Trace("set %h {%h}<br />\n", zName, zValue); } Th_SetVar(g.interp, zName, -1, zValue, nValue); Th_Free(g.interp, zValue); } } /* ** Store an integer value in a variable in the interpreter. */ void Th_StoreInt(const char *zName, int iValue){ Blob value; char *zValue; |
︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | } if( inBracket ){ if( z[0]!='>' ) return 0; i += 2; } return i; } /* ** The z[] input contains text mixed with TH1 scripts. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 | } if( inBracket ){ if( z[0]!='>' ) return 0; i += 2; } return i; } #ifdef FOSSIL_ENABLE_TH1_HOOKS /* ** This function determines if TH1 hooks are enabled for the repository. It ** may be necessary to open the repository and/or the configuration ("user") ** database from within this function. Before this function returns, any ** database opened will be closed again. This is very important because some ** commands do not expect the repository and/or the configuration ("user") ** database to be open prior to their own code doing so. */ int Th_AreHooksEnabled(void){ int rc; if( fossil_getenv("TH1_ENABLE_HOOKS")!=0 ){ return 1; } Th_OpenConfig(1); rc = db_get_boolean("th1-hooks", 0); Th_CloseConfig(1); return rc; } /* ** This function is called by Fossil just prior to dispatching a command. ** Returning a value other than TH_OK from this function (i.e. via an ** evaluated script raising an error or calling [break]/[continue]) will ** cause the actual command execution to be skipped. */ int Th_CommandHook( const char *zName, char cmdFlags ){ int rc = TH_OK; if( !Th_AreHooksEnabled() ) return rc; Th_FossilInit(TH_INIT_HOOK); Th_Store("cmd_name", zName); Th_StoreList("cmd_args", g.argv, g.argc); Th_StoreInt("cmd_flags", cmdFlags); rc = Th_Eval(g.interp, 0, "command_hook", -1); if( rc==TH_ERROR ){ int nResult = 0; char *zResult = (char*)Th_GetResult(g.interp, &nResult); /* ** Make sure that the TH1 script error was not caused by a "missing" ** command hook handler as that is not actually an error condition. */ if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){ sendError(zResult, nResult, 0); }else{ /* ** There is no command hook handler "installed". This situation ** is NOT actually an error. */ rc = TH_OK; } } /* ** If the script returned TH_ERROR (e.g. the "command_hook" TH1 command does ** not exist because commands are not being hooked), return TH_OK because we ** do not want to skip executing essential commands unless the called command ** (i.e. "command_hook") explicitly forbids this by successfully returning ** TH_BREAK or TH_CONTINUE. */ if( g.thTrace ){ Th_Trace("[command_hook {%h}] => %h<br />\n", zName, Th_ReturnCodeName(rc, 0)); } /* ** Does our call to Th_FossilInit() result in opening a database? If so, ** clean it up now. This is very important because some commands do not ** expect the repository and/or the configuration ("user") database to be ** open prior to their own code doing so. */ if( TH_INIT_HOOK & TH_INIT_NEED_CONFIG ) Th_CloseConfig(1); return rc; } /* ** This function is called by Fossil just after dispatching a command. ** Returning a value other than TH_OK from this function (i.e. via an ** evaluated script raising an error or calling [break]/[continue]) may ** cause an error message to be displayed to the local interactive user. ** Currently, TH1 error messages generated by this function are ignored. */ int Th_CommandNotify( const char *zName, char cmdFlags ){ int rc = TH_OK; if( !Th_AreHooksEnabled() ) return rc; Th_FossilInit(TH_INIT_HOOK); Th_Store("cmd_name", zName); Th_StoreList("cmd_args", g.argv, g.argc); Th_StoreInt("cmd_flags", cmdFlags); rc = Th_Eval(g.interp, 0, "command_notify", -1); if( g.thTrace ){ Th_Trace("[command_notify {%h}] => %h<br />\n", zName, Th_ReturnCodeName(rc, 0)); } /* ** Does our call to Th_FossilInit() result in opening a database? If so, ** clean it up now. This is very important because some commands do not ** expect the repository and/or the configuration ("user") database to be ** open prior to their own code doing so. */ if( TH_INIT_HOOK & TH_INIT_NEED_CONFIG ) Th_CloseConfig(1); return rc; } /* ** This function is called by Fossil just prior to processing a web page. ** Returning a value other than TH_OK from this function (i.e. via an ** evaluated script raising an error or calling [break]/[continue]) will ** cause the actual web page processing to be skipped. */ int Th_WebpageHook( const char *zName, char cmdFlags ){ int rc = TH_OK; if( !Th_AreHooksEnabled() ) return rc; Th_FossilInit(TH_INIT_HOOK); Th_Store("web_name", zName); Th_StoreList("web_args", g.argv, g.argc); Th_StoreInt("web_flags", cmdFlags); rc = Th_Eval(g.interp, 0, "webpage_hook", -1); if( rc==TH_ERROR ){ int nResult = 0; char *zResult = (char*)Th_GetResult(g.interp, &nResult); /* ** Make sure that the TH1 script error was not caused by a "missing" ** webpage hook handler as that is not actually an error condition. */ if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){ sendError(zResult, nResult, 1); }else{ /* ** There is no webpage hook handler "installed". This situation ** is NOT actually an error. */ rc = TH_OK; } } /* ** If the script returned TH_ERROR (e.g. the "webpage_hook" TH1 command does ** not exist because commands are not being hooked), return TH_OK because we ** do not want to skip processing essential web pages unless the called ** command (i.e. "webpage_hook") explicitly forbids this by successfully ** returning TH_BREAK or TH_CONTINUE. */ if( g.thTrace ){ Th_Trace("[webpage_hook {%h}] => %h<br />\n", zName, Th_ReturnCodeName(rc, 0)); } /* ** Does our call to Th_FossilInit() result in opening a database? If so, ** clean it up now. This is very important because some commands do not ** expect the repository and/or the configuration ("user") database to be ** open prior to their own code doing so. */ if( TH_INIT_HOOK & TH_INIT_NEED_CONFIG ) Th_CloseConfig(1); return rc; } /* ** This function is called by Fossil just after processing a web page. ** Returning a value other than TH_OK from this function (i.e. via an ** evaluated script raising an error or calling [break]/[continue]) may ** cause an error message to be displayed to the remote user. ** Currently, TH1 error messages generated by this function are ignored. */ int Th_WebpageNotify( const char *zName, char cmdFlags ){ int rc = TH_OK; if( !Th_AreHooksEnabled() ) return rc; Th_FossilInit(TH_INIT_HOOK); Th_Store("web_name", zName); Th_StoreList("web_args", g.argv, g.argc); Th_StoreInt("web_flags", cmdFlags); rc = Th_Eval(g.interp, 0, "webpage_notify", -1); if( g.thTrace ){ Th_Trace("[webpage_notify {%h}] => %h<br />\n", zName, Th_ReturnCodeName(rc, 0)); } /* ** Does our call to Th_FossilInit() result in opening a database? If so, ** clean it up now. This is very important because some commands do not ** expect the repository and/or the configuration ("user") database to be ** open prior to their own code doing so. */ if( TH_INIT_HOOK & TH_INIT_NEED_CONFIG ) Th_CloseConfig(1); return rc; } #endif #ifdef FOSSIL_ENABLE_TH1_DOCS /* ** This function determines if TH1 docs are enabled for the repository. */ int Th_AreDocsEnabled(void){ if( fossil_getenv("TH1_ENABLE_DOCS")!=0 ){ return 1; } return db_get_boolean("th1-docs", 0); } #endif /* ** The z[] input contains text mixed with TH1 scripts. ** The TH1 scripts are contained within <th1>...</th1>. ** TH1 variables are $aaa or $<aaa>. The first form of ** variable is literal. The second is run through htmlize ** before being inserted. ** ** This routine processes the template and writes the results ** on either stdout or into CGI. */ |
︙ | ︙ | |||
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 | sendText(z, i, 0); } return rc; } /* ** COMMAND: test-th-render */ void test_th_render(void){ Blob in; Th_InitTraceLog(); | > > > > > > > > > > > > > > > > > | < | > > > > > > > > > > > > > > > > > > | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 | sendText(z, i, 0); } return rc; } /* ** COMMAND: test-th-render ** ** Usage: %fossil test-th-render FILE ** ** Read the content of the file named "FILE" as if it were a header or ** footer or ticket rendering script, evaluate it, and show the results ** on standard output. ** ** Options: ** ** --cgi Include a CGI response header in the output ** --http Include an HTTP response header in the output ** --open-config Open the configuration database */ void test_th_render(void){ int forceCgi = 0, fullHttpReply = 0; Blob in; Th_InitTraceLog(); forceCgi = find_option("cgi", 0, 0)!=0; fullHttpReply = find_option("http", 0, 0)!=0; if( fullHttpReply ) forceCgi = 1; if( forceCgi ) Th_ForceCgi(fullHttpReply); if( find_option("open-config", 0, 0)!=0 ){ Th_OpenConfig(1); } verify_all_options(); if( g.argc<3 ){ usage("FILE"); } blob_zero(&in); blob_read_from_file(&in, g.argv[2]); Th_Render(blob_str(&in)); Th_PrintTraceLog(); if( forceCgi ) cgi_reply(); } /* ** COMMAND: test-th-eval ** ** Usage: %fossil test-th-eval SCRIPT ** ** Evaluate SCRIPT as if it were a header or footer or ticket rendering ** script, evaluate it, and show the results on standard output. ** ** Options: ** ** --cgi Include a CGI response header in the output ** --http Include an HTTP response header in the output ** --open-config Open the configuration database */ void test_th_eval(void){ int rc; const char *zRc; int forceCgi, fullHttpReply; Th_InitTraceLog(); forceCgi = find_option("cgi", 0, 0)!=0; fullHttpReply = find_option("http", 0, 0)!=0; if( fullHttpReply ) forceCgi = 1; if( forceCgi ) Th_ForceCgi(fullHttpReply); if( find_option("open-config", 0, 0)!=0 ){ Th_OpenConfig(1); } if( g.argc!=3 ){ usage("script"); } Th_FossilInit(TH_INIT_DEFAULT); rc = Th_Eval(g.interp, 0, g.argv[2], -1); zRc = Th_ReturnCodeName(rc, 1); fossil_print("%s%s%s\n", zRc, zRc ? ": " : "", Th_GetResult(g.interp, 0)); Th_PrintTraceLog(); if( forceCgi ) cgi_reply(); } #ifdef FOSSIL_ENABLE_TH1_HOOKS /* ** COMMAND: test-th-hook */ void test_th_hook(void){ int rc = TH_OK; int nResult = 0; char *zResult; int forceCgi, fullHttpReply; Th_InitTraceLog(); forceCgi = find_option("cgi", 0, 0)!=0; fullHttpReply = find_option("http", 0, 0)!=0; if( fullHttpReply ) forceCgi = 1; if( forceCgi ) Th_ForceCgi(fullHttpReply); if( g.argc<5 ){ usage("TYPE NAME FLAGS"); } if( fossil_stricmp(g.argv[2], "cmdhook")==0 ){ rc = Th_CommandHook(g.argv[3], (char)atoi(g.argv[4])); }else if( fossil_stricmp(g.argv[2], "cmdnotify")==0 ){ rc = Th_CommandNotify(g.argv[3], (char)atoi(g.argv[4])); }else if( fossil_stricmp(g.argv[2], "webhook")==0 ){ rc = Th_WebpageHook(g.argv[3], (char)atoi(g.argv[4])); }else if( fossil_stricmp(g.argv[2], "webnotify")==0 ){ rc = Th_WebpageNotify(g.argv[3], (char)atoi(g.argv[4])); }else{ fossil_fatal("Unknown TH1 hook %s\n", g.argv[2]); } zResult = (char*)Th_GetResult(g.interp, &nResult); sendText("RESULT (", -1, 0); sendText(Th_ReturnCodeName(rc, 0), -1, 0); sendText("): ", -1, 0); sendText(zResult, nResult, 0); sendText("\n", -1, 0); Th_PrintTraceLog(); if( forceCgi ) cgi_reply(); } #endif |
Changes to src/th_tcl.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | ** ** This file contains code used to bridge the TH1 and Tcl scripting languages. */ #include "config.h" #ifdef FOSSIL_ENABLE_TCL #include "th.h" #include "tcl.h" /* ** These macros are designed to reduce the redundant code required to marshal ** arguments from TH1 to Tcl. */ #define USE_ARGV_TO_OBJV() \ int objc; \ Tcl_Obj **objv; \ | > | | | | | | | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | ** ** This file contains code used to bridge the TH1 and Tcl scripting languages. */ #include "config.h" #ifdef FOSSIL_ENABLE_TCL #include "sqlite3.h" #include "th.h" #include "tcl.h" /* ** These macros are designed to reduce the redundant code required to marshal ** arguments from TH1 to Tcl. */ #define USE_ARGV_TO_OBJV() \ int objc; \ Tcl_Obj **objv; \ int obji; #define COPY_ARGV_TO_OBJV() \ objc = argc-1; \ objv = (Tcl_Obj **)ckalloc((unsigned)(objc * sizeof(Tcl_Obj *))); \ for(obji=1; obji<argc; obji++){ \ objv[obji-1] = Tcl_NewStringObj(argv[obji], argl[obji]); \ Tcl_IncrRefCount(objv[obji-1]); \ } #define FREE_ARGV_TO_OBJV() \ for(obji=1; obji<argc; obji++){ \ Tcl_DecrRefCount(objv[obji-1]); \ } \ ckfree((char *)objv); /* ** Fetch the Tcl interpreter from the specified void pointer, cast to a Tcl ** context. */ #define GET_CTX_TCL_INTERP(ctx) \ |
︙ | ︙ | |||
107 108 109 110 111 112 113 | # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # endif /* defined(__CYGWIN__) */ # endif /* defined(_WIN32) */ # ifndef TCL_FINDEXECUTABLE_NAME | | | | | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # endif /* defined(__CYGWIN__) */ # endif /* defined(_WIN32) */ # ifndef TCL_FINDEXECUTABLE_NAME # define TCL_FINDEXECUTABLE_NAME "_Tcl_FindExecutable\0" # endif # ifndef TCL_CREATEINTERP_NAME # define TCL_CREATEINTERP_NAME "_Tcl_CreateInterp\0" # endif # ifndef TCL_DELETEINTERP_NAME # define TCL_DELETEINTERP_NAME "_Tcl_DeleteInterp\0" # endif # ifndef TCL_FINALIZE_NAME # define TCL_FINALIZE_NAME "_Tcl_Finalize\0" # endif #endif /* defined(USE_TCL_STUBS) */ /* ** The function types for Tcl_FindExecutable and Tcl_CreateInterp are needed ** when the Tcl library is being loaded dynamically by a stubs-enabled ** application (i.e. the inverse of using a stubs-enabled package). These are |
︙ | ︙ | |||
163 164 165 166 167 168 169 | */ #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) /* ** HACK: Using some preprocessor magic and a private static variable, redirect ** the Tcl API calls [found within this file] to the function pointers ** that will be contained in our private Tcl stubs table. This takes ** advantage of the fact that the Tcl headers always define the Tcl API | | > | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | */ #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS) /* ** HACK: Using some preprocessor magic and a private static variable, redirect ** the Tcl API calls [found within this file] to the function pointers ** that will be contained in our private Tcl stubs table. This takes ** advantage of the fact that the Tcl headers always define the Tcl API ** functions in terms of the "tclStubsPtr" variable when the define ** USE_TCL_STUBS is present during compilation. */ #define tclStubsPtr privateTclStubsPtr static const TclStubs *tclStubsPtr = NULL; /* ** Create a Tcl interpreter structure that mirrors just enough fields to get ** it up and running successfully with our private implementation of the Tcl |
︙ | ︙ | |||
246 247 248 249 250 251 252 253 254 255 256 257 258 259 | /* ** Creates and initializes a Tcl interpreter for use with the specified TH1 ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied ** by the caller. This must be declared here because quite a few functions in ** this file need to use it before it can be defined. */ static int createTclInterp(Th_Interp *interp, void *pContext); /* ** Returns the Tcl interpreter result as a string with the associated length. ** If the Tcl interpreter or the Tcl result are NULL, the length will be 0. ** If the length pointer is NULL, the length will not be stored. */ static char *getTclResult( | > > > > > > > > > > > > > > > > > > > > > > | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | /* ** Creates and initializes a Tcl interpreter for use with the specified TH1 ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied ** by the caller. This must be declared here because quite a few functions in ** this file need to use it before it can be defined. */ static int createTclInterp(Th_Interp *interp, void *pContext); /* ** Returns a name for a Tcl return code. */ static const char *getTclReturnCodeName( int rc, int nullIfOk ){ static char zRc[32]; switch( rc ){ case TCL_OK: return nullIfOk ? 0 : "TCL_OK"; case TCL_ERROR: return "TCL_ERROR"; case TCL_BREAK: return "TCL_BREAK"; case TCL_RETURN: return "TCL_RETURN"; case TCL_CONTINUE: return "TCL_CONTINUE"; default: { sqlite3_snprintf(sizeof(zRc), zRc, "Tcl return code %d", rc); } } return zRc; } /* ** Returns the Tcl interpreter result as a string with the associated length. ** If the Tcl interpreter or the Tcl result are NULL, the length will be 0. ** If the length pointer is NULL, the length will not be stored. */ static char *getTclResult( |
︙ | ︙ | |||
309 310 311 312 313 314 315 | const char **argv, int *argl, int rc ){ struct TclContext *tclContext = (struct TclContext *)ctx; tcl_NotifyProc *xNotifyProc; | | | | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | const char **argv, int *argl, int rc ){ struct TclContext *tclContext = (struct TclContext *)ctx; tcl_NotifyProc *xNotifyProc; if( !tclContext ){ Th_ErrorMessage(interp, "invalid Tcl context", (const char *)"", 0); return TH_ERROR; } xNotifyProc = bIsPost ? tclContext->xPostEval : tclContext->xPreEval; if( xNotifyProc ){ rc = xNotifyProc(bIsPost ? tclContext->pPostContext : tclContext->pPreContext, interp, ctx, argc, argv, argl, rc); } return rc; } |
︙ | ︙ | |||
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 | if( !resultObjPtr ){ rc = TCL_ERROR; } } Tcl_DecrRefCount(listPtr); return rc; } /* ** Creates and initializes a Tcl interpreter for use with the specified TH1 ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied ** by the caller. */ static int createTclInterp( Th_Interp *interp, void *pContext ){ struct TclContext *tclContext = (struct TclContext *)pContext; int argc; char **argv; char *argv0 = 0; Tcl_Interp *tclInterp; char *setup; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 | if( !resultObjPtr ){ rc = TCL_ERROR; } } Tcl_DecrRefCount(listPtr); return rc; } /* ** Evaluate a Tcl script, creating the Tcl interpreter if necessary. If the ** Tcl script succeeds, start a Tcl event loop until there are no more events ** remaining to process -OR- the script calls [exit]. If the bWait argument ** is zero, only process events that are already in the queue; otherwise, ** process events until the script terminates the Tcl event loop. */ void fossil_print(const char *zFormat, ...); /* printf.h */ int evaluateTclWithEvents( Th_Interp *interp, void *pContext, const char *zScript, int nScript, int bWait, int bVerbose ){ struct TclContext *tclContext = (struct TclContext *)pContext; Tcl_Interp *tclInterp; int rc; int flags = TCL_ALL_EVENTS; if( createTclInterp(interp, pContext)!=TH_OK ){ return TH_ERROR; } tclInterp = tclContext->interp; rc = Tcl_EvalEx(tclInterp, zScript, nScript, TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ if( bVerbose ){ const char *zResult = getTclResult(tclInterp, 0); fossil_print("%s: ", getTclReturnCodeName(rc, 0)); fossil_print("%s\n", zResult); } return rc; } if( !bWait ) flags |= TCL_DONT_WAIT; while( Tcl_DoOneEvent(flags) ){ /* do nothing */ } return rc; } /* ** Creates and initializes a Tcl interpreter for use with the specified TH1 ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied ** by the caller. */ static int createTclInterp( Th_Interp *interp, void *pContext ){ struct TclContext *tclContext = (struct TclContext *)pContext; int argc; char **argv; char *argv0 = 0; Tcl_Interp *tclInterp; char *setup; if( !tclContext ){ Th_ErrorMessage(interp, "invalid Tcl context", (const char *)"", 0); return TH_ERROR; } if( tclContext->interp ){ return TH_OK; } if( loadTcl(interp, &tclContext->library, &tclContext->xFindExecutable, &tclContext->xCreateInterp, &tclContext->xDeleteInterp, &tclContext->xFinalize)!=TH_OK ){ return TH_ERROR; } |
︙ | ︙ | |||
880 881 882 883 884 885 886 | struct TclContext *tclContext = (struct TclContext *)pContext; Tcl_Interp *tclInterp; tcl_FinalizeProc *xFinalize; #if defined(USE_TCL_STUBS) void *library; #endif /* defined(USE_TCL_STUBS) */ | | | | 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 | struct TclContext *tclContext = (struct TclContext *)pContext; Tcl_Interp *tclInterp; tcl_FinalizeProc *xFinalize; #if defined(USE_TCL_STUBS) void *library; #endif /* defined(USE_TCL_STUBS) */ if( !tclContext ){ Th_ErrorMessage(interp, "invalid Tcl context", (const char *)"", 0); return TH_ERROR; } /* ** Grab the Tcl_Finalize function pointer prior to deleting the Tcl ** interpreter because the memory backing the Tcl stubs table will ** be going away. */ xFinalize = tclContext->xFinalize; /* ** If the Tcl interpreter has been created, formally delete it now. */ tclInterp = tclContext->interp; if( tclInterp ){ Tcl_DeleteInterp(tclInterp); tclContext->interp = tclInterp = 0; } /* ** If the Tcl library is not finalized prior to unloading it, a deadlock ** can occur in some circumstances (i.e. the [clock] thread is running). */ |
︙ | ︙ | |||
937 938 939 940 941 942 943 | void *pContext ){ int i; /* Add the Tcl integration commands to TH1. */ for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){ void *ctx; | | | 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 | void *pContext ){ int i; /* Add the Tcl integration commands to TH1. */ for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){ void *ctx; if( !aCommand[i].zName || !aCommand[i].xProc ) continue; ctx = aCommand[i].pContext; /* Use Tcl interpreter for context? */ if( !ctx ) ctx = pContext; Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0); } return TH_OK; } #endif /* FOSSIL_ENABLE_TCL */ |
Changes to src/timeline.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement the timeline web page ** */ #include <string.h> #include <time.h> | > < | < < | < < < < < < < < < | < > > > > > > > | | < < | | < < < < < < < < < < < < < | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to implement the timeline web page ** */ #include "config.h" #include <string.h> #include <time.h> #include "timeline.h" /* ** The value of one second in julianday notation */ #define ONE_SECOND (1.0/86400.0) /* ** Add an appropriate tag to the output if "rid" is unpublished (private) */ #define UNPUB_TAG "<em>(unpublished)</em>" void tag_private_status(int rid){ if( content_is_private(rid) ){ cgi_printf("%s", UNPUB_TAG); } } /* ** Generate a hyperlink to a version. */ void hyperlink_to_uuid(const char *zUuid){ if( g.perm.Hyperlink ){ @ %z(xhref("class='timelineHistLink'","%R/info/%!S",zUuid))[%S(zUuid)]</a> }else{ @ <span class="timelineHistDsp">[%S(zUuid)]</span> } } /* ** Generate a hyperlink to a date & time. */ void hyperlink_to_date(const char *zDate, const char *zSuffix){ |
︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | #define TIMELINE_BRIEF 0x0004 /* Combine adjacent elements of same object */ #define TIMELINE_GRAPH 0x0008 /* Compute a graph */ #define TIMELINE_DISJOINT 0x0010 /* Elements are not contiguous */ #define TIMELINE_FCHANGES 0x0020 /* Detail file changes */ #define TIMELINE_BRCOLOR 0x0040 /* Background color by branch name */ #define TIMELINE_UCOLOR 0x0080 /* Background color by user */ #define TIMELINE_FRENAMES 0x0100 /* Detail only file name changes */ #endif /* ** Hash a string and use the hash to determine a background color. */ char *hash_color(const char *z){ int i; /* Loop counter */ unsigned int h = 0; /* Hash on the branch name */ int r, g, b; /* Values for red, green, and blue */ int h1, h2, h3, h4; /* Elements of the hash value */ int mx, mn; /* Components of HSV */ static char zColor[10]; /* The resulting color */ static int ix[2] = {0,0}; /* Color chooser parameters */ if( ix[0]==0 ){ | > > | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | #define TIMELINE_BRIEF 0x0004 /* Combine adjacent elements of same object */ #define TIMELINE_GRAPH 0x0008 /* Compute a graph */ #define TIMELINE_DISJOINT 0x0010 /* Elements are not contiguous */ #define TIMELINE_FCHANGES 0x0020 /* Detail file changes */ #define TIMELINE_BRCOLOR 0x0040 /* Background color by branch name */ #define TIMELINE_UCOLOR 0x0080 /* Background color by user */ #define TIMELINE_FRENAMES 0x0100 /* Detail only file name changes */ #define TIMELINE_UNHIDE 0x0200 /* Unhide check-ins with "hidden" tag */ #define TIMELINE_SHOWRID 0x0400 /* Show RID values in addition to UUIDs */ #endif /* ** Hash a string and use the hash to determine a background color. */ char *hash_color(const char *z){ int i; /* Loop counter */ unsigned int h = 0; /* Hash on the branch name */ int r, g, b; /* Values for red, green, and blue */ int h1, h2, h3, h4; /* Elements of the hash value */ int mx, mn; /* Components of HSV */ static char zColor[10]; /* The resulting color */ static int ix[2] = {0,0}; /* Color chooser parameters */ if( ix[0]==0 ){ if( skin_detail_boolean("white-foreground") ){ ix[0] = 140; ix[1] = 40; }else{ ix[0] = 216; ix[1] = 16; } } |
︙ | ︙ | |||
179 180 181 182 183 184 185 | ** testing the hash_color() function. */ void test_hash_color_page(void){ const char *zBr; char zNm[10]; int i, cnt; login_check_credentials(); | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | ** testing the hash_color() function. */ void test_hash_color_page(void){ const char *zBr; char zNm[10]; int i, cnt; login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } style_header("Hash Color Test"); for(i=cnt=0; i<10; i++){ sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); zBr = P(zNm); if( zBr && zBr[0] ){ @ <p style='border:1px solid;background-color:%s(hash_color(zBr));'> |
︙ | ︙ | |||
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | ** 10. Short comment to user for repeated tickets and wiki */ void www_print_timeline( Stmt *pQuery, /* Query to implement the timeline */ int tmFlags, /* Flags controlling display behavior */ const char *zThisUser, /* Suppress links to this user */ const char *zThisTag, /* Suppress links to this tag */ void (*xExtra)(int) /* Routine to call on each line of display */ ){ int mxWikiLen; Blob comment; int prevTagid = 0; int suppressCnt = 0; char zPrevDate[20]; GraphContext *pGraph = 0; int prevWasDivider = 0; /* True if previous output row was <hr> */ int fchngQueryInit = 0; /* True if fchngQuery is initialized */ Stmt fchngQuery; /* Query for file changes on check-ins */ static Stmt qbranch; int pendingEndTr = 0; /* True if a </td></tr> is needed */ int vid = 0; /* Current checkout version */ | > > > | > > > | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | ** 10. Short comment to user for repeated tickets and wiki */ void www_print_timeline( Stmt *pQuery, /* Query to implement the timeline */ int tmFlags, /* Flags controlling display behavior */ const char *zThisUser, /* Suppress links to this user */ const char *zThisTag, /* Suppress links to this tag */ int selectedRid, /* Highlight the line with this RID value */ void (*xExtra)(int) /* Routine to call on each line of display */ ){ int mxWikiLen; Blob comment; int prevTagid = 0; int suppressCnt = 0; char zPrevDate[20]; GraphContext *pGraph = 0; int prevWasDivider = 0; /* True if previous output row was <hr> */ int fchngQueryInit = 0; /* True if fchngQuery is initialized */ Stmt fchngQuery; /* Query for file changes on check-ins */ static Stmt qbranch; int pendingEndTr = 0; /* True if a </td></tr> is needed */ int vid = 0; /* Current checkout version */ int dateFormat = 0; /* 0: HH:MM (default) */ const char *zDateFmt; if( fossil_strcmp(g.zIpAddr, "127.0.0.1")==0 && db_open_local(0) ){ vid = db_lget_int("checkout", 0); } zPrevDate[0] = 0; mxWikiLen = db_get_int("timeline-max-comment", 0); dateFormat = db_get_int("timeline-date-format", 0); zDateFmt = P("datefmt"); if( zDateFmt ) dateFormat = atoi(zDateFmt); if( tmFlags & TIMELINE_GRAPH ){ pGraph = graph_init(); /* style is not moved to css, because this is ** a technical div for the timeline graph */ @ <div id="canvas" style="position:relative;height:0px;width:0px;" @ onclick="clickOnGraph(event)"></div> |
︙ | ︙ | |||
280 281 282 283 284 285 286 | const char *zUser = db_column_text(pQuery, 4); const char *zTagList = db_column_text(pQuery, 8); int tagid = db_column_int(pQuery, 9); const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; const char *zBr = 0; /* Branch */ int commentColumn = 3; /* Column containing comment text */ int modPending; /* Pending moderation */ | | > > > > > > | > > > > > > > > | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | const char *zUser = db_column_text(pQuery, 4); const char *zTagList = db_column_text(pQuery, 8); int tagid = db_column_int(pQuery, 9); const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; const char *zBr = 0; /* Branch */ int commentColumn = 3; /* Column containing comment text */ int modPending; /* Pending moderation */ char zTime[20]; if( zDate==0 ){ zDate = "YYYY-MM-DD HH:MM:SS"; /* Something wrong with the repo */ } modPending = moderation_pending(rid); if( tagid ){ if( modPending ) tagid = -tagid; if( tagid==prevTagid ){ if( tmFlags & TIMELINE_BRIEF ){ suppressCnt++; continue; }else{ commentColumn = 10; } } } prevTagid = tagid; if( suppressCnt ){ @ <span class="timelineDisabled">... %d(suppressCnt) similar @ event%s(suppressCnt>1?"s":"") omitted.</span> suppressCnt = 0; } if( pendingEndTr ){ @ </td></tr> if( pendingEndTr>1 ){ @ <tr class="timelineSpacer"></tr> } pendingEndTr = 0; } if( fossil_strcmp(zType,"div")==0 ){ if( !prevWasDivider ){ @ <tr><td colspan="3"><hr class="timelineMarker"/></td></tr> } prevWasDivider = 1; continue; } prevWasDivider = 0; /* Date format codes: ** (0) HH:MM ** (1) HH:MM:SS ** (2) YYYY-MM-DD HH:MM ** (3) YYMMDD HH:MM ** (4) (off) */ if( dateFormat<2 ){ if( fossil_strnicmp(zDate, zPrevDate, 10) ){ sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate); @ <tr><td> @ <div class="divider timelineDate">%s(zPrevDate)</div> @ </td><td></td><td></td></tr> } memcpy(zTime, &zDate[11], 5+dateFormat*3); zTime[5+dateFormat*3] = 0; }else if( 2==dateFormat ){ /* YYYY-MM-DD HH:MM */ sqlite3_snprintf(sizeof(zTime), zTime, "%.16s", zDate); }else if( 3==dateFormat ){ /* YYMMDD HH:MM */ int pos = 0; zTime[pos++] = zDate[2]; zTime[pos++] = zDate[3]; /* YY */ zTime[pos++] = zDate[5]; zTime[pos++] = zDate[6]; /* MM */ zTime[pos++] = zDate[8]; zTime[pos++] = zDate[9]; /* DD */ zTime[pos++] = ' '; zTime[pos++] = zDate[11]; zTime[pos++] = zDate[12]; /* HH */ zTime[pos++] = ':'; zTime[pos++] = zDate[14]; zTime[pos++] = zDate[15]; /* MM */ zTime[pos++] = 0; }else{ zTime[0] = 0; } pendingEndTr = 1; if( rid==selectedRid ){ @ <tr class="timelineSpacer"></tr> @ <tr class="timelineSelected"> pendingEndTr = 2; }else if( rid==vid ){ @ <tr class="timelineCurrent"> }else { @ <tr> } @ <td class="timelineTime">%s(zTime)</td> @ <td class="timelineGraph"> if( tmFlags & TIMELINE_UCOLOR ) zBgClr = zUser ? hash_color(zUser) : 0; |
︙ | ︙ | |||
348 349 350 351 352 353 354 | }else{ zBgClr = hash_color(zBr); } } } if( zType[0]=='c' && (pGraph || (tmFlags & TIMELINE_BRCOLOR)!=0) ){ int nParent = 0; | | | | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | }else{ zBgClr = hash_color(zBr); } } } if( zType[0]=='c' && (pGraph || (tmFlags & TIMELINE_BRCOLOR)!=0) ){ int nParent = 0; int aParent[GR_MAX_RAIL]; int gidx; static Stmt qparent; db_static_prepare(&qparent, "SELECT pid FROM plink" " WHERE cid=:rid AND pid NOT IN phantom" " ORDER BY isprim DESC /*sort*/" ); db_bind_int(&qparent, ":rid", rid); while( db_step(&qparent)==SQLITE_ROW && nParent<ArraySize(aParent) ){ aParent[nParent++] = db_column_int(&qparent, 0); } db_reset(&qparent); gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr, zUuid, isLeaf); db_reset(&qbranch); @ <div id="m%d(gidx)"></div> } @</td> if( zBgClr && zBgClr[0] && rid!=selectedRid ){ @ <td class="timelineTableCell" style="background-color: %h(zBgClr);"> }else{ @ <td class="timelineTableCell"> } if( pGraph && zType[0]!='c' ){ @ • } |
︙ | ︙ | |||
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | @ <span class="timelineLeaf">Leaf:</span> } } }else if( zType[0]=='e' && tagid ){ hyperlink_to_event_tagid(tagid<0?-tagid:tagid); }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ hyperlink_to_uuid(zUuid); } db_column_blob(pQuery, commentColumn, &comment); if( zType[0]!='c' ){ /* Comments for anything other than a check-in are generated by ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ wiki_convert(&comment, 0, WIKI_INLINE); }else if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ Blob truncated; blob_zero(&truncated); blob_append(&truncated, blob_buffer(&comment), mxWikiLen); blob_append(&truncated, "...", 3); | > > > | | | | | | | > | > > > > > > > > > | > > > > | | | | | | | | > < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > > > > > > > | | | | > > > > > > | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 | @ <span class="timelineLeaf">Leaf:</span> } } }else if( zType[0]=='e' && tagid ){ hyperlink_to_event_tagid(tagid<0?-tagid:tagid); }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ hyperlink_to_uuid(zUuid); } if( tmFlags & TIMELINE_SHOWRID ){ @ (%d(rid)) } db_column_blob(pQuery, commentColumn, &comment); if( zType[0]!='c' ){ /* Comments for anything other than a check-in are generated by ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ wiki_convert(&comment, 0, WIKI_INLINE); }else if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ Blob truncated; blob_zero(&truncated); blob_append(&truncated, blob_buffer(&comment), mxWikiLen); blob_append(&truncated, "...", 3); @ <span class="timelineComment">%W(blob_str(&truncated))</span> blob_reset(&truncated); }else{ @ <span class="timelineComment">%W(blob_str(&comment))</span> } blob_reset(&comment); /* Generate the "user: USERNAME" at the end of the comment, together ** with a hyperlink to another timeline for that user. */ if( zTagList && zTagList[0]==0 ) zTagList = 0; if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){ char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd&n=200", zDispUser, zDate); @ (user: %z(href("%z",zLink))%h(zDispUser)</a>%s(zTagList?",":"\051") }else{ @ (user: %h(zDispUser)%s(zTagList?",":"\051") } /* Generate a "detail" link for tags. */ if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){ @ [%z(href("%R/info/%!S",zUuid))details</a>] } /* Generate the "tags: TAGLIST" at the end of the comment, together ** with hyperlinks to the tag list. */ if( zTagList ){ if( g.perm.Hyperlink ){ int i; const char *z = zTagList; Blob links; blob_zero(&links); while( z && z[0] ){ for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){} if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){ blob_appendf(&links, "%z%#h</a>%.2s", href("%R/timeline?r=%#t&nd&c=%t&n=200",i,z,zDate), i,z, &z[i] ); }else{ blob_appendf(&links, "%#h", i+2, z); } if( z[i]==0 ) break; z += i+2; } @ tags: %s(blob_str(&links))) blob_reset(&links); }else{ @ tags: %h(zTagList)) } } tag_private_status(rid); /* Generate extra hyperlinks at the end of the comment */ if( xExtra ){ xExtra(rid); } /* Generate the file-change list if requested */ if( (tmFlags & (TIMELINE_FCHANGES|TIMELINE_FRENAMES))!=0 && zType[0]=='c' && g.perm.Hyperlink ){ int inUl = 0; if( !fchngQueryInit ){ db_prepare(&fchngQuery, "SELECT (pid==0) AS isnew," " fid," " (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name," " (SELECT uuid FROM blob WHERE rid=fid)," " (SELECT uuid FROM blob WHERE rid=pid)," " (SELECT name FROM filename WHERE fnid=mlink.pfnid) AS oldnm" " FROM mlink" " WHERE mid=:mid AND (pid!=fid OR pfnid>0)" " AND (fid>0 OR" " fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=:mid))" " AND NOT mlink.isaux" " ORDER BY 3 /*sort*/" ); fchngQueryInit = 1; } db_bind_int(&fchngQuery, ":mid", rid); while( db_step(&fchngQuery)==SQLITE_ROW ){ const char *zFilename = db_column_text(&fchngQuery, 2); int isNew = db_column_int(&fchngQuery, 0); int fid = db_column_int(&fchngQuery, 1); int isDel = fid==0; const char *zOldName = db_column_text(&fchngQuery, 5); const char *zOld = db_column_text(&fchngQuery, 4); const char *zNew = db_column_text(&fchngQuery, 3); const char *zUnpub = ""; char *zA; char zId[20]; if( !inUl ){ @ <ul class="filelist"> inUl = 1; } if( tmFlags & TIMELINE_SHOWRID ){ sqlite3_snprintf(sizeof(zId), zId, " (%d) ", fid); }else{ zId[0] = 0; } if( (tmFlags & TIMELINE_FRENAMES)!=0 ){ if( !isNew && !isDel && zOldName!=0 ){ @ <li> %h(zOldName) → %h(zFilename)%s(zId) } continue; } zA = href("%R/artifact/%!S",fid?zNew:zOld); if( content_is_private(fid) ){ zUnpub = UNPUB_TAG; } if( isNew ){ @ <li> %s(zA)%h(zFilename)</a>%s(zId) %s(zUnpub) (new file) @ %z(href("%R/artifact/%!S",zNew))[view]</a></li> }else if( isDel ){ @ <li> %s(zA)%h(zFilename)</a> (deleted)</li> }else if( fossil_strcmp(zOld,zNew)==0 && zOldName!=0 ){ @ <li> %h(zOldName) → %s(zA)%h(zFilename)</a>%s(zId) @ %s(zUnpub) %z(href("%R/artifact/%!S",zNew))[view]</a></li> }else{ if( zOldName!=0 ){ @ <li>%h(zOldName) → %s(zA)%h(zFilename)%s(zId)</a> %s(zUnpub) }else{ @ <li>%s(zA)%h(zFilename)</a>%s(zId) %s(zUnpub) } @ %z(href("%R/fdiff?sbs=1&v1=%!S&v2=%!S",zOld,zNew))[diff]</a></li> } fossil_free(zA); } db_reset(&fchngQuery); if( inUl ){ @ </ul> } } } if( suppressCnt ){ @ <span class="timelineDisabled">... %d(suppressCnt) similar @ event%s(suppressCnt>1?"s":"") omitted.</span> suppressCnt = 0; } if( pendingEndTr ){ @ </td></tr> } if( pGraph ){ graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0); if( pGraph->nErr ){ graph_free(pGraph); pGraph = 0; }else{ int w; /* style is not moved to css, because this is ** a technical div for the timeline graph */ w = pGraph->mxRail*pGraph->iRailPitch + 28; @ <tr><td></td><td> @ <div id="grbtm" style="width:%d(w)px;"></div> @ </td><td></td></tr> } } @ </table> if( fchngQueryInit ) db_finalize(&fchngQuery); timeline_output_graph_javascript(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0, 0); } /* ** Change the RGB background color given in the argument in a foreground ** color with the same hue. */ static const char *bg_to_fg(const char *zIn){ int i; unsigned int x[3]; unsigned int mx = 0; static int whiteFg = -1; static char zRes[10]; if( strlen(zIn)!=7 || zIn[0]!='#' ) return zIn; zIn++; for(i=0; i<3; i++){ x[i] = hex_digit_value(zIn[0])*16 + hex_digit_value(zIn[1]); zIn += 2; if( x[i]>mx ) mx = x[i]; } if( whiteFg<0 ) whiteFg = skin_detail_boolean("white-foreground"); if( whiteFg ){ /* Make the color lighter */ static const unsigned int t = 215; if( mx<t ) for(i=0; i<3; i++) x[i] += t - mx; }else{ /* Make the color darker */ static const unsigned int t = 128; if( mx>t ) for(i=0; i<3; i++) x[i] -= mx - t; } sqlite3_snprintf(sizeof(zRes),zRes,"#%02x%02x%02x",x[0],x[1],x[2]); return zRes; } /* ** Generate all of the necessary javascript to generate a timeline ** graph. */ void timeline_output_graph_javascript( GraphContext *pGraph, /* The graph to be displayed */ int omitDescenders, /* True to omit descenders */ int fileDiff /* True for file diff. False for check-in diff */ ){ if( pGraph && pGraph->nErr==0 && pGraph->nRow>0 ){ GraphRow *pRow; int i; char cSep; int mergeOffset; /* Pixel offset from rail to merge riser */ int iRailPitch; /* Pixels between consecutive rails */ int showArrowheads; /* True to draw arrowheads. False to omit. */ int circleNodes; /* True for circle nodes. False for square nodes */ int colorGraph; /* Use colors for graph lines */ iRailPitch = pGraph->iRailPitch; showArrowheads = skin_detail_boolean("timeline-arrowheads"); circleNodes = skin_detail_boolean("timeline-circle-nodes"); colorGraph = skin_detail_boolean("timeline-color-graph-lines"); /* Number of pixels that the thin merge lines are offset from the ** the center of the think rail lines. If zero, then the vertical ** merge lines overlap with the thicker rail lines. */ mergeOffset = iRailPitch>=14 ? 4 : iRailPitch>=13 ? 3 : 0; if( PB("nomo") ) mergeOffset = 0; @ <script> @ var railPitch=%d(iRailPitch); /* the rowinfo[] array contains all the information needed to generate ** the graph. Each entry contains information for a single row: ** ** id: The id of the <div> element for the row. This is an integer. ** to get an actual id, prepend "m" to the integer. The top node ** is 1 and numbers increase moving down the timeline. ** bg: The background color for this row ** r: The "rail" that the node for this row sits on. The left-most ** rail is 0 and the number increases to the right. ** d: True if there is a "descender" - an arrow coming from the bottom ** of the page straight up to this node. ** mo: "merge-out". If non-zero, this is one more than the x-coordinate ** for the upward portion of a merge arrow. The merge arrow goes up ** to the row identified by mu:. If this value is zero then ** node has no merge children and no merge-out line is drawn. ** mu: The id of the row which is the top of the merge-out arrow. ** u: Draw a thick child-line out of the top of this node and up to ** the node with an id equal to this value. 0 if it is straight to ** the top of the page, -1 if there is no thick-line riser. ** f: 0x01: a leaf node. ** au: An array of integers that define thick-line risers for branches. ** The integers are in pairs. For each pair, the first integer is ** is the rail on which the riser should run and the second integer ** is the id of the node upto which the riser should run. ** mi: "merge-in". An array of integer x-coordinates from which ** merge arrows should be drawn into this node. If the value is ** negative, then the x-coordinate is the absolute value of mi[] ** and a thin merge-arrow descender is drawn to the bottom of ** the screen. ** h: The SHA1 hash of the object being graphed */ cgi_printf("var rowinfo = [\n"); for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){ int mo = pRow->mergeOut; if( mo<0 ){ mo = 0; }else{ int x = (mo/4)*iRailPitch; switch( mo&3 ){ case 0: x -= mergeOffset-2; break; case 1: x += 1; break; case 2: x += mergeOffset+1; break; } mo = x; } cgi_printf("{id:%d,bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,f:%d,au:", pRow->idx, /* id */ pRow->zBgClr, /* bg */ pRow->iRail, /* r */ pRow->bDescender, /* d */ mo, /* mo */ |
︙ | ︙ | |||
630 631 632 633 634 635 636 | if( i==pRow->iRail ) continue; if( pRow->aiRiser[i]>0 ){ cgi_printf("%c%d,%d", cSep, i, pRow->aiRiser[i]); cSep = ','; } } if( cSep=='[' ) cgi_printf("["); | | > > > > > > | | | | | | | | | 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | if( i==pRow->iRail ) continue; if( pRow->aiRiser[i]>0 ){ cgi_printf("%c%d,%d", cSep, i, pRow->aiRiser[i]); cSep = ','; } } if( cSep=='[' ) cgi_printf("["); cgi_printf("],"); if( colorGraph && pRow->zBgClr[0]=='#' ){ cgi_printf("fg:\"%s\",", bg_to_fg(pRow->zBgClr)); } /* mi */ cgi_printf("mi:"); cSep = '['; for(i=0; i<GR_MAX_RAIL; i++){ if( pRow->mergeIn[i] ){ int mi = i*iRailPitch; if( pRow->mergeIn[i]==1 ) mi -= mergeOffset-1; if( pRow->mergeIn[i]==3 ) mi += mergeOffset; if( pRow->mergeDown & (1<<i) ) mi = -mi; cgi_printf("%c%d", cSep, mi); cSep = ','; } } if( cSep=='[' ) cgi_printf("["); cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n"); } cgi_printf("var nrail = %d\n", pGraph->mxRail+1); graph_free(pGraph); @ var cDiv = gebi("canvas"); @ var csty = window.getComputedStyle && window.getComputedStyle(cDiv,null); @ var lineClr = (csty && csty.getPropertyValue('color')) || 'black'; @ var bgClr = (csty && csty.getPropertyValue('background-color')) ||'white'; @ if( bgClr=='transparent' ) bgClr = 'white'; @ var boxColor = lineClr; @ function drawBox(color,x0,y0,x1,y1){ @ var n = document.createElement("div"); @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } @ var w = x1-x0+1; @ var h = y1-y0+1; @ n.style.position = "absolute"; @ n.style.overflow = "hidden"; @ n.style.left = x0+"px"; @ n.style.top = y0+"px"; @ n.style.width = w+"px"; @ n.style.height = h+"px"; @ n.style.backgroundColor = color; @ cDiv.appendChild(n); @ return n; @ } @ function absoluteY(id){ @ var obj = gebi(id); @ if( !obj ) return; @ var top = 0; @ if( obj.offsetParent ){ |
︙ | ︙ | |||
690 691 692 693 694 695 696 | @ if( obj.offsetParent ){ @ do{ @ left += obj.offsetLeft; @ }while( obj = obj.offsetParent ); @ } @ return left; @ } | > | | > > > > > > > > > > > > > > > | | < | | < | > | > > > > > > > > > > > > > > > > > | | | > | | | > > | > | > > > | | | | > | | | > | | > > > > > > > > > > > > > > | 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 | @ if( obj.offsetParent ){ @ do{ @ left += obj.offsetLeft; @ }while( obj = obj.offsetParent ); @ } @ return left; @ } if( showArrowheads ){ @ function drawUpArrow(x,y0,y1,clr){ @ drawBox(clr,x,y0+4,x+1,y1); @ var n = document.createElement("div"), @ l = x-2, @ t = y0; @ n.style.position = "absolute"; @ n.style.left = l+"px"; @ n.style.top = t+"px"; @ n.style.width = 0; @ n.style.height = 0; @ n.style.transform = "scale(.999)"; @ n.style.borderWidth = 0; @ n.style.borderStyle = "solid"; @ n.style.borderColor = "transparent"; @ n.style.borderRightWidth = "3px"; @ n.style.borderBottomColor = clr; @ n.style.borderLeftWidth = "3px"; @ if( y0+10>=y1 ){ @ n.style.borderBottomWidth = "5px"; @ } else { @ n.style.borderBottomWidth = "7px"; @ } @ cDiv.appendChild(n); @ } }else{ @ function drawUpArrow(x,y0,y1,clr){ @ drawBox(clr,x,y0+1,x+1,y1); @ } } @ function drawThinArrow(y,xFrom,xTo){ @ var n = document.createElement("div"), @ t = y-2; @ n.style.position = "absolute"; @ n.style.top = t+"px"; @ n.style.width = 0; @ n.style.height = "1px"; @ n.style.transform = "scale(.999)"; @ n.style.borderWidth = 0; @ n.style.borderStyle = "solid"; @ n.style.borderColor = "transparent"; @ n.style.borderTopWidth = "2px"; @ n.style.borderBottomWidth = "2px"; @ if( xFrom<xTo ){ @ drawBox(lineClr,xFrom,y,xTo-3,y); @ n.style.left = xTo-3+"px"; @ n.style.borderLeftWidth = "3px"; @ n.style.borderLeftColor = lineClr; @ }else{ @ drawBox(lineClr,xTo+3,y,xFrom,y); @ n.style.left = xTo+1+"px"; @ n.style.borderRightWidth = "3px"; @ n.style.borderRightColor = lineClr; @ } @ cDiv.appendChild(n); @ } @ function drawThinLine(x0,y0,x1,y1){ @ drawBox(lineClr,x0,y0,x1,y1); @ } @ function drawNodeBox(color,x0,y0,x1,y1){ @ var n = drawBox(color,x0,y0,x1,y1); @ n.style.cursor = "pointer"; if( circleNodes ){ @ n.style.borderRadius = "6px"; } @ } @ function drawNode(p, left, btm){ @ drawNodeBox(boxColor,p.x-5,p.y-5,p.x+6,p.y+6); @ drawNodeBox(p.bg||bgClr,p.x-4,p.y-4,p.x+5,p.y+5); @ if( p.u>0 ) drawUpArrow(p.x,rowinfo[p.u-1].y+6,p.y-6,p.fg||lineClr); @ if( p.f&1 ) drawNodeBox(boxColor,p.x-1,p.y-1,p.x+2,p.y+2); if( !omitDescenders ){ @ if( p.u==0 ) drawUpArrow(p.x,0,p.y-6,p.fg||lineClr); @ if( p.d ) drawUpArrow(p.x,p.y+6,btm,p.fg||lineClr); } @ if( p.mo>0 ){ @ var x1 = p.mo + left - 1; @ var y1 = p.y-3; @ var x0 = x1>p.x ? p.x+7 : p.x-6; @ var u = rowinfo[p.mu-1]; @ var y0 = u.y+5; @ if( x1>=p.x-5 && x1<=p.x+5 ){ @ y1 = p.y-5; @ }else{ @ drawThinLine(x0,y1,x1,y1); @ } if( mergeOffset==0 ) cgi_printf("if( p.mo!=p.u-1 ) "); @ drawThinLine(x1,y0,x1,y1); @ } @ var n = p.au.length; @ for(var i=0; i<n; i+=2){ @ var x1 = p.au[i]*railPitch + left; @ var x0 = x1>p.x ? p.x+7 : p.x-6; @ var u = rowinfo[p.au[i+1]-1]; @ if(u.id<p.id){ @ drawBox(u.fg||lineClr,x0,p.y,x1+1,p.y+1); @ drawUpArrow(x1,u.y+6,p.y,u.fg||lineClr); @ }else{ @ drawBox("#600000",x0,p.y,x1,p.y+1); @ drawBox("#600000",x1-1,p.y,x1,u.y+1); @ drawBox("#600000",x1,u.y,u.x-10,u.y+1); @ var n = document.createElement("div"), @ t = u.y-2, @ l = u.x-11; @ n.style.position = "absolute"; @ n.style.top = t+"px"; @ n.style.left = l+"px"; @ n.style.width = 0; @ n.style.height = 0; @ n.style.transform = "scale(.999)"; @ n.style.borderWidth = 0; @ n.style.borderStyle = "solid"; @ n.style.borderColor = "transparent"; @ n.style.borderTopWidth = "3px"; @ n.style.borderBottomWidth = "3px"; @ n.style.borderLeftWidth = "7px"; @ n.style.borderLeftColor = "#600000"; @ cDiv.appendChild(n); @ } @ } @ for(var j in p.mi){ @ var y0 = p.y+5; @ var mx = p.mi[j]; @ if( mx<0 ){ @ mx = left-mx; |
︙ | ︙ | |||
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 | @ break; @ } @ } @ } @ function clickOnRow(p){ @ if( selRow==null ){ @ selBox = drawBox("red",p.x-2,p.y-2,p.x+3,p.y+3); @ selRow = p; @ }else if( selRow==p ){ @ var canvasDiv = gebi("canvas"); @ canvasDiv.removeChild(selBox); @ selBox = null; @ selRow = null; @ }else{ if( fileDiff ){ @ location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h+"&sbs=1"; }else{ | > > > > > > | > < | 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 | @ break; @ } @ } @ } @ function clickOnRow(p){ @ if( selRow==null ){ @ selBox = drawBox("red",p.x-2,p.y-2,p.x+3,p.y+3); if( circleNodes ){ @ selBox.style.borderRadius="6px"; } @ selRow = p; @ }else if( selRow==p ){ @ var canvasDiv = gebi("canvas"); @ canvasDiv.removeChild(selBox); @ selBox = null; @ selRow = null; @ }else{ if( fileDiff ){ @ location.href="%R/fdiff?v1="+selRow.h+"&v2="+p.h+"&sbs=1"; }else{ if( db_get_boolean("show-version-diffs", 0)==0 ){ @ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=0"; }else{ @ location.href="%R/vdiff?from="+selRow.h+"&to="+p.h+"&sbs=1"; } } @ } @ } @ var lastId = "m"+rowinfo[rowinfo.length-1].id; @ var lastY = 0; @ function checkHeight(){ @ var h = absoluteY(lastId); @ if( h!=lastY ){ @ renderGraph(); @ lastY = h; @ } @ setTimeout("checkHeight();", 1000); @ } @ checkHeight(); @ </script> } } /* ** Create a temporary table suitable for storing timeline data. */ |
︙ | ︙ | |||
868 869 870 871 872 873 874 | @ etype TEXT, @ taglist TEXT, @ tagid INTEGER, @ short TEXT, @ sortby REAL @ ) ; | | | | | | 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 | @ etype TEXT, @ taglist TEXT, @ tagid INTEGER, @ short TEXT, @ sortby REAL @ ) ; db_multi_exec("%s", zSql/*safe-for-%s*/); } /* ** Return a pointer to a constant string that forms the basis ** for a timeline query for the WWW interface. */ const char *timeline_query_for_www(void){ static const char *zBase = 0; static const char zBaseSql[] = @ SELECT @ blob.rid AS blobRid, @ uuid AS uuid, @ datetime(event.mtime%s) AS timestamp, @ coalesce(ecomment, comment) AS comment, @ coalesce(euser, user) AS user, @ blob.rid IN leaf AS leaf, @ bgcolor AS bgColor, @ event.type AS eventType, @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0) AS tags, @ tagid AS tagid, @ brief AS brief, @ event.mtime AS mtime @ FROM event CROSS JOIN blob @ WHERE blob.rid=event.objid ; if( zBase==0 ){ zBase = mprintf(zBaseSql /*works-like: "%s"*/, timeline_utc()); } return zBase; } /* ** Generate a submenu element with a single parameter change. */ |
︙ | ︙ | |||
929 930 931 932 933 934 935 | double mtime; int rid; if( z==0 ) return -1.0; if( fossil_isdate(z) ){ mtime = db_double(0.0, "SELECT julianday(%Q,'utc')", z); if( mtime>0.0 ) return mtime; } | | | | > | > > > > > > | > | < < < < | < < | < < < < | | < < > | < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > | < | > | > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | | | < | < | | > | > < > > | > > > > > > | < | | > | | | | | | | > > > > > > > > > > | | | | > > > > > | < < | | | | | | | < | | | > > > > > > > | | < > > > > > | | < < | > | | | | | | | < | | > > > > > > | < | | < | < < | | < > > | > | | | | > > | < > > > > | | < < | | | | | | | 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 | double mtime; int rid; if( z==0 ) return -1.0; if( fossil_isdate(z) ){ mtime = db_double(0.0, "SELECT julianday(%Q,'utc')", z); if( mtime>0.0 ) return mtime; } rid = symbolic_name_to_rid(z, "*"); if( rid ){ mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); }else{ mtime = db_double(-1.0, "SELECT max(event.mtime) FROM event, tag, tagxref" " WHERE tag.tagname GLOB 'event-%q*'" " AND tagxref.tagid=tag.tagid AND tagxref.tagtype" " AND event.objid=tagxref.rid", z ); } return mtime; } /* ** zDate is a localtime date. Insert records into the ** "timeline" table to cause <hr> to be inserted on zDate. */ static int timeline_add_divider(double rDate){ int rid = db_int(-1, "SELECT rid FROM timeline ORDER BY abs(sortby-%.16g) LIMIT 1", rDate ); if( rid>0 ) return rid; db_multi_exec( "INSERT INTO timeline(rid,sortby,etype) VALUES(-1,%.16g,'div')", rDate ); return -1; } /* ** Return all possible names for file zUuid. */ char *names_of_file(const char *zUuid){ Stmt q; Blob out; const char *zSep = ""; db_prepare(&q, "SELECT DISTINCT filename.name FROM mlink, filename" " WHERE mlink.fid=(SELECT rid FROM blob WHERE uuid=%Q)" " AND filename.fnid=mlink.fnid", zUuid ); blob_zero(&out); while( db_step(&q)==SQLITE_ROW ){ const char *zFN = db_column_text(&q, 0); blob_appendf(&out, "%s%z%h</a>", zSep, href("%R/finfo?name=%t", zFN), zFN); zSep = " or "; } db_finalize(&q); return blob_str(&out); } /* ** Add the select/option box to the timeline submenu that is used to ** set the y= parameter that determines which elements to display ** on the timeline. */ static void timeline_y_submenu(int isDisabled){ static int i = 0; static const char *az[12]; if( i==0 ){ az[0] = "all"; az[1] = "Any Type"; i = 2; if( g.perm.Read ){ az[i++] = "ci"; az[i++] = "Check-ins"; az[i++] = "g"; az[i++] = "Tags"; } if( g.perm.RdWiki ){ az[i++] = "e"; az[i++] = "Tech Notes"; } if( g.perm.RdTkt ){ az[i++] = "t"; az[i++] = "Tickets"; } if( g.perm.RdWiki ){ az[i++] = "w"; az[i++] = "Wiki"; } assert( i<=ArraySize(az) ); } if( i>2 ){ style_submenu_multichoice("y", i/2, az, isDisabled); } } /* ** WEBPAGE: timeline ** ** Query parameters: ** ** a=TIMEORTAG after this event ** b=TIMEORTAG before this event ** c=TIMEORTAG "circa" this event ** m=TIMEORTAG mark this event ** n=COUNT max number of events in output ** p=UUID artifact and up to COUNT parents and ancestors ** d=UUID artifact and up to COUNT descendants ** dp=UUID The same as d=UUID&p=UUID ** t=TAGID show only check-ins with the given tagid ** r=TAGID show check-ins related to tagid ** u=USER only if belonging to this user ** y=TYPE 'ci', 'w', 't', 'e', or (default) 'all' ** s=TEXT string search (comment and brief) ** ng Suppress the graph if present ** nd Suppress "divider" lines ** v Show details of files changed ** f=UUID Show family (immediate parents and children) of UUID ** from=UUID Path from... ** to=UUID ... to this ** shortest ... show only the shortest path ** uf=FUUID Show only check-ins that use given file version ** brbg Background color from branch name ** ubg Background color from user ** namechng Show only check-ins that filename changes ** ym=YYYY-MM Shown only events for the given year/month. ** datefmt=N Override the date format ** ** p= and d= can appear individually or together. If either p= or d= ** appear, then u=, y=, a=, and b= are ignored. ** ** If both a= and b= appear then both upper and lower bounds are honored. ** ** If n= is missing, the default count is 50 for most queries but ** drops to 11 for c= queries. */ void page_timeline(void){ Stmt q; /* Query used to generate the timeline */ Blob sql; /* text of SQL used to generate timeline */ Blob desc; /* Description of the timeline */ int nEntry; /* Max number of entries on timeline */ int p_rid = name_to_typed_rid(P("p"),"ci"); /* artifact p and its parents */ int d_rid = name_to_typed_rid(P("d"),"ci"); /* artifact d and descendants */ int f_rid = name_to_typed_rid(P("f"),"ci"); /* artifact f and close family */ const char *zUser = P("u"); /* All entries by this user if not NULL */ const char *zType = PD("y","all"); /* Type of events. All if NULL */ const char *zAfter = P("a"); /* Events after this time */ const char *zBefore = P("b"); /* Events before this time */ const char *zCirca = P("c"); /* Events near this time */ const char *zMark = P("m"); /* Mark this event or an event this time */ const char *zTagName = P("t"); /* Show events with this tag */ const char *zBrName = P("r"); /* Show events related to this tag */ const char *zSearch = P("s"); /* Search string */ const char *zUses = P("uf"); /* Only show check-ins hold this file */ const char *zYearMonth = P("ym"); /* Show check-ins for the given YYYY-MM */ const char *zYearWeek = P("yw"); /* Check-ins for YYYY-WW (week-of-year) */ int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ int renameOnly = P("namechng")!=0; /* Show only check-ins that rename files */ int tagid; /* Tag ID */ int tmFlags = 0; /* Timeline flags */ const char *zThisTag = 0; /* Suppress links to this tag */ const char *zThisUser = 0; /* Suppress links to this user */ HQuery url; /* URL for various branch links */ int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */ int noMerge = P("shortest")==0; /* Follow merge links if shorter */ int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ int pd_rid; double rBefore, rAfter, rCirca; /* Boundary times */ const char *z; char *zOlderButton = 0; /* URL for Older button at the bottom */ int selectedRid = -9999999; /* Show a highlight on this RID */ int disableY = 0; /* Disable type selector on submenu */ /* Set number of rows to display */ z = P("n"); if( z ){ if( fossil_strcmp(z,"all")==0 ){ nEntry = 0; }else{ nEntry = atoi(z); if( nEntry<=0 ){ cgi_replace_query_parameter("n","10"); nEntry = 10; } } }else if( zCirca ){ cgi_replace_query_parameter("n","11"); nEntry = 11; }else{ cgi_replace_query_parameter("n","50"); nEntry = 50; } /* To view the timeline, must have permission to read project data. */ pd_rid = name_to_typed_rid(P("dp"),"ci"); if( pd_rid ){ p_rid = d_rid = pd_rid; } login_check_credentials(); if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){ login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); return; } url_initialize(&url, "timeline"); cgi_query_parameters_to_url(&url); if( zTagName && g.perm.Read ){ tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName); zThisTag = zTagName; }else if( zBrName && g.perm.Read ){ tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",zBrName); zThisTag = zBrName; }else{ tagid = 0; } if( zMark && zMark[0]==0 ){ if( zAfter ) zMark = zAfter; if( zBefore ) zMark = zBefore; if( zCirca ) zMark = zCirca; } if( tagid>0 && db_int(0,"SELECT count(*) FROM tagxref WHERE tagid=%d",tagid)<=nEntry ){ nEntry = -1; zCirca = 0; } if( zType[0]=='a' ){ tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH; }else{ tmFlags |= TIMELINE_GRAPH; } if( PB("ng") || zSearch!=0 ){ tmFlags &= ~TIMELINE_GRAPH; } if( PB("brbg") ){ tmFlags |= TIMELINE_BRCOLOR; } if( PB("unhide") ){ tmFlags |= TIMELINE_UNHIDE; } if( PB("ubg") ){ tmFlags |= TIMELINE_UCOLOR; } if( zUses!=0 ){ int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses); if( ufid ){ zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid); db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)"); compute_uses_file("usesfile", ufid, 0); zType = "ci"; disableY = 1; }else{ zUses = 0; } } if( renameOnly ){ db_multi_exec( "CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);" "INSERT OR IGNORE INTO rnfile" " SELECT mid FROM mlink WHERE pfnid>0 AND pfnid!=fnid;" ); disableY = 1; } style_header("Timeline"); login_anonymous_available(); timeline_temp_table(); blob_zero(&sql); blob_zero(&desc); blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1); blob_append(&sql, timeline_query_for_www(), -1); if( PB("fc") || PB("v") || PB("detail") ){ tmFlags |= TIMELINE_FCHANGES; } if( (tmFlags & TIMELINE_UNHIDE)==0 ){ blob_append_sql(&sql, " AND NOT EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n", TAG_HIDDEN ); } if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.perm.Read ){ /* If from= and to= are present, display all nodes on a path connecting ** the two */ PathNode *p = 0; const char *zFrom = 0; const char *zTo = 0; if( from_rid && to_rid ){ p = path_shortest(from_rid, to_rid, noMerge, 0); zFrom = P("from"); zTo = P("to"); }else{ if( path_common_ancestor(me_rid, you_rid) ){ p = path_first(); } zFrom = P("me"); zTo = P("you"); } blob_append(&sql, " AND event.objid IN (0", -1); while( p ){ blob_append_sql(&sql, ",%d", p->rid); p = p->u.pTo; } blob_append(&sql, ")", -1); path_reset(); blob_append(&desc, "All nodes on the path from ", -1); blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h", zFrom), zFrom); blob_append(&desc, " to ", -1); blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo); tmFlags |= TIMELINE_DISJOINT; db_multi_exec("%s", blob_sql_text(&sql)); }else if( (p_rid || d_rid) && g.perm.Read ){ /* If p= or d= is present, ignore all other parameters other than n= */ char *zUuid; int np, nd; tmFlags |= TIMELINE_DISJOINT; if( p_rid && d_rid ){ if( p_rid!=d_rid ) p_rid = d_rid; if( P("n")==0 ) nEntry = 10; } db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" ); zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", p_rid ? p_rid : d_rid); blob_append_sql(&sql, " AND event.objid IN ok"); nd = 0; if( d_rid ){ compute_descendants(d_rid, nEntry+1); nd = db_int(0, "SELECT count(*)-1 FROM ok"); if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); if( useDividers ) selectedRid = d_rid; db_multi_exec("DELETE FROM ok"); } if( p_rid ){ compute_ancestors(p_rid, nEntry+1, 0); np = db_int(0, "SELECT count(*)-1 FROM ok"); if( np>0 ){ if( nd>0 ) blob_appendf(&desc, " and "); blob_appendf(&desc, "%d ancestors", np); db_multi_exec("%s", blob_sql_text(&sql)); } if( useDividers ) selectedRid = p_rid; } blob_appendf(&desc, " of %z[%S]</a>", href("%R/info/%!S", zUuid), zUuid); if( d_rid ){ if( p_rid ){ /* If both p= and d= are set, we don't have the uuid of d yet. */ zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid); } } style_submenu_entry("n","Max:",4,0); timeline_y_submenu(1); style_submenu_binary("v","With Files","Without Files", zType[0]!='a' && zType[0]!='c'); }else if( f_rid && g.perm.Read ){ /* If f= is present, ignore all other parameters other than n= */ char *zUuid; db_multi_exec( "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);" "INSERT INTO ok VALUES(%d);" "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;" "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", f_rid, f_rid, f_rid ); blob_append_sql(&sql, " AND event.objid IN ok"); db_multi_exec("%s", blob_sql_text(&sql)); if( useDividers ) selectedRid = f_rid; blob_appendf(&desc, "Parents and children of check-in "); zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid); blob_appendf(&desc, "%z[%S]</a>", href("%R/info/%!S", zUuid), zUuid); tmFlags |= TIMELINE_DISJOINT; style_submenu_binary("v","With Files","Without Files", zType[0]!='a' && zType[0]!='c'); if( (tmFlags & TIMELINE_UNHIDE)==0 ){ timeline_submenu(&url, "Unhide", "unhide", "", 0); } }else{ /* Otherwise, a timeline based on a span of time */ int n, nBefore, nAfter; const char *zEType = "timeline item"; char *zDate; if( zUses ){ blob_append_sql(&sql, " AND event.objid IN usesfile "); } if( renameOnly ){ blob_append_sql(&sql, " AND event.objid IN rnfile "); } if( zYearMonth ){ blob_append_sql(&sql, " AND %Q=strftime('%%Y-%%m',event.mtime) ", zYearMonth); } else if( zYearWeek ){ blob_append_sql(&sql, " AND %Q=strftime('%%Y-%%W',event.mtime) ", zYearWeek); } if( tagid>0 ){ blob_append_sql(&sql, " AND (EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n", tagid); if( zBrName ){ /* The next two blob_appendf() calls add SQL that causes check-ins that ** are not part of the branch which are parents or children of the ** branch to be included in the report. This related check-ins are ** useful in helping to visualize what has happened on a quiescent ** branch that is infrequently merged with a much more activate branch. */ blob_append_sql(&sql, " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=cid" " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)\n", tagid ); if( (tmFlags & TIMELINE_UNHIDE)==0 ){ blob_append_sql(&sql, " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid" " WHERE tagid=%d AND tagtype>0 AND pid=blob.rid)\n", TAG_HIDDEN ); } if( P("mionly")==0 ){ blob_append_sql(&sql, " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=pid" " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)\n", tagid ); if( (tmFlags & TIMELINE_UNHIDE)==0 ){ blob_append_sql(&sql, " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid" " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)\n", TAG_HIDDEN ); } } } blob_append_sql(&sql, ")"); } if( (zType[0]=='w' && !g.perm.RdWiki) || (zType[0]=='t' && !g.perm.RdTkt) || (zType[0]=='e' && !g.perm.RdWiki) || (zType[0]=='c' && !g.perm.Read) || (zType[0]=='g' && !g.perm.Read) ){ zType = "all"; } if( zType[0]=='a' ){ if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){ char cSep = '('; blob_append_sql(&sql, " AND event.type IN "); if( g.perm.Read ){ blob_append_sql(&sql, "%c'ci','g'", cSep); cSep = ','; } if( g.perm.RdWiki ){ blob_append_sql(&sql, "%c'w','e'", cSep); cSep = ','; } if( g.perm.RdTkt ){ blob_append_sql(&sql, "%c't'", cSep); cSep = ','; } blob_append_sql(&sql, ")"); } }else{ /* zType!="all" */ blob_append_sql(&sql, " AND event.type=%Q", zType); if( zType[0]=='c' ){ zEType = "check-in"; }else if( zType[0]=='w' ){ zEType = "wiki edit"; }else if( zType[0]=='t' ){ zEType = "ticket change"; }else if( zType[0]=='e' ){ zEType = "technical note"; }else if( zType[0]=='g' ){ zEType = "tag"; } } if( zUser ){ int n = db_int(0,"SELECT count(*) FROM event" " WHERE user=%Q OR euser=%Q", zUser, zUser); if( n<=nEntry ){ zCirca = zBefore = zAfter = 0; nEntry = -1; } blob_append_sql(&sql, " AND (event.user=%Q OR event.euser=%Q)", zUser, zUser); zThisUser = zUser; } if( zSearch ){ blob_append_sql(&sql, " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", zSearch, zSearch); } rBefore = symbolic_name_to_mtime(zBefore); rAfter = symbolic_name_to_mtime(zAfter); rCirca = symbolic_name_to_mtime(zCirca); if( rAfter>0.0 ){ if( rBefore>0.0 ){ blob_append_sql(&sql, " AND event.mtime>=%.17g AND event.mtime<=%.17g" " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND); nEntry = -1; }else{ blob_append_sql(&sql, " AND event.mtime>=%.17g ORDER BY event.mtime ASC", rAfter-ONE_SECOND); } zCirca = 0; url_add_parameter(&url, "c", 0); }else if( rBefore>0.0 ){ blob_append_sql(&sql, " AND event.mtime<=%.17g ORDER BY event.mtime DESC", rBefore+ONE_SECOND); zCirca = 0; url_add_parameter(&url, "c", 0); }else if( rCirca>0.0 ){ Blob sql2; blob_init(&sql2, blob_sql_text(&sql), -1); blob_append_sql(&sql2, " AND event.mtime<=%f ORDER BY event.mtime DESC", rCirca); if( nEntry>0 ){ blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2); nEntry -= (nEntry+1)/2; } if( PB("showsql") ){ @ <pre>%h(blob_sql_text(&sql2))</pre> } db_multi_exec("%s", blob_sql_text(&sql2)); blob_reset(&sql2); blob_append_sql(&sql, " AND event.mtime>=%f ORDER BY event.mtime ASC", rCirca ); if( zMark==0 ) zMark = zCirca; }else{ blob_append_sql(&sql, " ORDER BY event.mtime DESC"); } if( nEntry>0 ) blob_append_sql(&sql, " LIMIT %d", nEntry); db_multi_exec("%s", blob_sql_text(&sql)); n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/"); if( zYearMonth ){ blob_appendf(&desc, "%s events for %h", zEType, zYearMonth); }else if( zYearWeek ){ blob_appendf(&desc, "%s events for year/week %h", zEType, zYearWeek); }else if( zBefore==0 && zCirca==0 && n>=nEntry && nEntry>0 ){ blob_appendf(&desc, "%d most recent %ss", n, zEType); }else{ blob_appendf(&desc, "%d %ss", n, zEType); } if( zUses ){ char *zFilenames = names_of_file(zUses); blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames, href("%R/artifact/%!S",zUses), zUses); tmFlags |= TIMELINE_DISJOINT; } if( renameOnly ){ blob_appendf(&desc, " that contain filename changes"); tmFlags |= TIMELINE_DISJOINT|TIMELINE_FRENAMES; } if( zUser ){ |
︙ | ︙ | |||
1412 1413 1414 1415 1416 1417 1418 | }else if( rCirca>0.0 ){ blob_appendf(&desc, " occurring around %h.<br />", zCirca); } if( zSearch ){ blob_appendf(&desc, " matching \"%h\"", zSearch); } if( g.perm.Hyperlink ){ | > > > > > > > > > > > > > > > > > | | | > | | | | | | < < < < < | | > | < < | < > | < > | < | | | < > | | | | < | > > | < < < < < < < < | > > > > > > | > > > | > > | < > > | | > > > > > > > | < > | | | > > > > | > | | | 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 | }else if( rCirca>0.0 ){ blob_appendf(&desc, " occurring around %h.<br />", zCirca); } if( zSearch ){ blob_appendf(&desc, " matching \"%h\"", zSearch); } if( g.perm.Hyperlink ){ if( zCirca && rCirca ){ nBefore = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div'" " AND sortby<=%f /*scan*/", rCirca); nAfter = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div'" " AND sortby>=%f /*scan*/", rCirca); zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); if( nBefore>=nEntry ){ timeline_submenu(&url, "Older", "b", zDate, "c"); zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "c", 0)); } if( nAfter>=nEntry ){ timeline_submenu(&url, "Newer", "a", zDate, "c"); } free(zDate); }else{ if( zAfter || n==nEntry ){ zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); timeline_submenu(&url, "Older", "b", zDate, "a"); zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0)); free(zDate); } if( zBefore || (zAfter && n==nEntry) ){ zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); timeline_submenu(&url, "Newer", "a", zDate, "b"); free(zDate); } } if( zType[0]=='a' || zType[0]=='c' ){ if( (tmFlags & TIMELINE_UNHIDE)==0 ){ timeline_submenu(&url, "Unhide", "unhide", "", 0); } } style_submenu_entry("n","Max:",4,0); timeline_y_submenu(disableY); style_submenu_binary("v","With Files","Without Files", zType[0]!='a' && zType[0]!='c'); } } if( PB("showsql") ){ @ <pre>%h(blob_sql_text(&sql))</pre> } if( search_restrict(SRCH_CKIN)!=0 ){ style_submenu_element("Search", 0, "%R/search?y=c"); } if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; if( useDividers && zMark && zMark[0] ){ double r = symbolic_name_to_mtime(zMark); if( r>0.0 ) selectedRid = timeline_add_divider(r); } blob_zero(&sql); db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); @ <h2>%b(&desc)</h2> blob_reset(&desc); www_print_timeline(&q, tmFlags, zThisUser, zThisTag, selectedRid, 0); db_finalize(&q); if( zOlderButton ){ @ %z(xhref("class='button'","%z",zOlderButton))Older</a> } style_footer(); } /* ** The input query q selects various records. Print a human-readable ** summary of those records. ** ** Limit number of lines or entries printed to nLimit. If nLimit is zero ** there is no limit. If nLimit is greater than zero, limit the number of ** complete entries printed. If nLimit is less than zero, attempt to limit ** the number of lines printed (this is basically the legacy behavior). ** The line limit, if used, is approximate because it is only checked on a ** per-entry basis. If verbose mode, the file name details are considered ** to be part of the entry. ** ** The query should return these columns: ** ** 0. rid ** 1. uuid ** 2. Date/Time ** 3. Comment string and user ** 4. Number of non-merge children ** 5. Number of parents ** 6. mtime ** 7. branch */ void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){ int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit; int nLine = 0; int nEntry = 0; char zPrevDate[20]; const char *zCurrentUuid = 0; int fchngQueryInit = 0; /* True if fchngQuery is initialized */ Stmt fchngQuery; /* Query for file changes on check-ins */ int rc; zPrevDate[0] = 0; if( g.localOpen ){ int rid = db_lget_int("checkout", 0); zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); } while( (rc=db_step(q))==SQLITE_ROW ){ int rid = db_column_int(q, 0); const char *zId = db_column_text(q, 1); const char *zDate = db_column_text(q, 2); const char *zCom = db_column_text(q, 3); int nChild = db_column_int(q, 4); int nParent = db_column_int(q, 5); char *zFree = 0; int n = 0; char zPrefix[80]; if( nAbsLimit!=0 ){ if( nLimit<0 && nLine>=nAbsLimit ){ fossil_print("--- line limit (%d) reached ---\n", nAbsLimit); break; /* line count limit hit, stop. */ }else if( nEntry>=nAbsLimit ){ fossil_print("--- entry limit (%d) reached ---\n", nAbsLimit); break; /* entry count limit hit, stop. */ } } if( fossil_strnicmp(zDate, zPrevDate, 10) ){ fossil_print("=== %.10s ===\n", zDate); memcpy(zPrevDate, zDate, 10); nLine++; /* record another line */ } if( zCom==0 ) zCom = ""; fossil_print("%.8s ", &zDate[11]); zPrefix[0] = 0; if( nParent>1 ){ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* "); n = strlen(zPrefix); } if( nChild>1 ){ const char *zBrType; if( count_nonbranch_children(rid)>1 ){ zBrType = "*FORK* "; }else{ zBrType = "*BRANCH* "; } sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], zBrType); n = strlen(zPrefix); } if( fossil_strcmp(zCurrentUuid,zId)==0 ){ sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*CURRENT* "); n += strlen(zPrefix+n); } if( content_is_private(rid) ){ sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* "); n += strlen(zPrefix+n); } zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom); /* record another X lines */ nLine += comment_print(zFree, zCom, 9, width, g.comFmtFlags); fossil_free(zFree); if(verboseFlag){ if( !fchngQueryInit ){ db_prepare(&fchngQuery, "SELECT (pid==0) AS isnew," " (fid==0) AS isdel," " (SELECT name FROM filename WHERE fnid=mlink.fnid) AS name," |
︙ | ︙ | |||
1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 | if( isNew ){ fossil_print(" ADDED %s\n",zFilename); }else if( isDel ){ fossil_print(" DELETED %s\n",zFilename); }else{ fossil_print(" EDITED %s\n", zFilename); } } db_reset(&fchngQuery); } } if( fchngQueryInit ) db_finalize(&fchngQuery); } /* ** Return a pointer to a static string that forms the basis for ** a timeline query for display on a TTY. */ const char *timeline_query_for_tty(void){ static const char zBaseSql[] = @ SELECT @ blob.rid AS rid, @ uuid, | > > > > > > > > > > | | | | | | > > > > | | > > > > > | > > > > > > > > > | | > > > > > > > > > > > > > > | > | 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 | if( isNew ){ fossil_print(" ADDED %s\n",zFilename); }else if( isDel ){ fossil_print(" DELETED %s\n",zFilename); }else{ fossil_print(" EDITED %s\n", zFilename); } nLine++; /* record another line */ } db_reset(&fchngQuery); } nEntry++; /* record another complete entry */ } if( rc==SQLITE_DONE ){ /* Did the underlying query actually have all entries? */ if( nAbsLimit==0 ){ fossil_print("+++ end of timeline (%d) +++\n", nEntry); }else{ fossil_print("+++ no more data (%d) +++\n", nEntry); } } if( fchngQueryInit ) db_finalize(&fchngQuery); } /* ** Return a pointer to a static string that forms the basis for ** a timeline query for display on a TTY. */ const char *timeline_query_for_tty(void){ static const char zBaseSql[] = @ SELECT @ blob.rid AS rid, @ uuid, @ datetime(event.mtime%s) AS mDateTime, @ coalesce(ecomment,comment) @ || ' (user: ' || coalesce(euser,user,'?') @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x @ FROM tag, tagxref @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) @ || ')' as comment, @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) @ AS primPlinkCount, @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount, @ event.mtime AS mtime, @ tagxref.value AS branch @ FROM tag CROSS JOIN event CROSS JOIN blob @ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid @ AND tagxref.tagtype>0 @ AND tagxref.rid=blob.rid @ WHERE blob.rid=event.objid @ AND tag.tagname='branch' ; return mprintf(zBaseSql /*works-like: "%s"*/, timeline_utc()); } /* ** Return true if the input string is a date in the ISO 8601 format: ** YYYY-MM-DD. */ static int isIsoDate(const char *z){ return strlen(z)==10 && z[4]=='-' && z[7]=='-' && fossil_isdigit(z[0]) && fossil_isdigit(z[5]); } /* ** COMMAND: timeline ** ** Usage: %fossil timeline ?WHEN? ?CHECKIN|DATETIME? ?OPTIONS? ** ** Print a summary of activity going backwards in date and time ** specified or from the current date and time if no arguments ** are given. The WHEN argument can be any unique abbreviation ** of one of these keywords: ** ** before ** after ** descendants | children ** ancestors | parents ** ** The CHECKIN can be any unique prefix of 4 characters or more. ** The DATETIME should be in the ISO8601 format. For ** examples: "2007-08-18 07:21:21". You can also say "current" ** for the current version or "now" for the current time. ** ** Options: ** -n|--limit N Output the first N entries (default 20 lines). ** N=0 means no limit. ** -p|--path PATH Output items affecting PATH only. ** PATH can be a file or a sub directory. ** --offset P skip P changes ** -t|--type TYPE Output items from the given types only, such as: ** ci = file commits only ** e = technical notes only ** t = tickets only ** w = wiki commits only ** -v|--verbose Output the list of files changed by each commit ** and the type of each change (edited, deleted, ** etc.) after the check-in comment. ** -W|--width <num> Width of lines (default is to auto-detect). Must be ** >20 or 0 (= no limit, resulting in a single line per ** entry). ** -R REPO_FILE Specifies the repository db to use. Default is ** the current checkout's repository. */ void timeline_cmd(void){ Stmt q; int n, k, width; const char *zLimit; const char *zWidth; const char *zOffset; const char *zType; char *zOrigin; char *zDate; Blob sql; int objid = 0; Blob uuid; int mode = 0 ; /* 0:none 1: before 2:after 3:children 4:parents */ int verboseFlag = 0 ; int iOffset; const char *zFilePattern = 0; Blob treeName; verboseFlag = find_option("verbose","v", 0)!=0; if( !verboseFlag){ verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */ } db_find_and_open_repository(0, 0); zLimit = find_option("limit","n",1); zWidth = find_option("width","W",1); zType = find_option("type","t",1); zFilePattern = find_option("path","p",1); if( !zLimit ){ zLimit = find_option("count",0,1); } if( zLimit ){ n = atoi(zLimit); }else{ n = -20; } if( zWidth ){ width = atoi(zWidth); if( (width!=0) && (width<=20) ){ fossil_fatal("-W|--width value must be >20 or 0"); } }else{ width = -1; } zOffset = find_option("offset",0,1); iOffset = zOffset ? atoi(zOffset) : 0; /* We should be done with options.. */ verify_all_options(); if( g.argc>=4 ){ k = strlen(g.argv[2]); if( strncmp(g.argv[2],"before",k)==0 ){ mode = 1; }else if( strncmp(g.argv[2],"after",k)==0 && k>1 ){ mode = 2; }else if( strncmp(g.argv[2],"descendants",k)==0 ){ mode = 3; }else if( strncmp(g.argv[2],"children",k)==0 ){ mode = 3; }else if( strncmp(g.argv[2],"ancestors",k)==0 && k>1 ){ mode = 4; }else if( strncmp(g.argv[2],"parents",k)==0 ){ mode = 4; }else if(!zType && !zLimit){ usage("?WHEN? ?CHECKIN|DATETIME? ?-n|--limit #? ?-t|--type TYPE? " "?-W|--width WIDTH? ?-p|--path PATH"); } if( '-' != *g.argv[3] ){ zOrigin = g.argv[3]; }else{ zOrigin = "now"; } }else if( g.argc==3 ){ |
︙ | ︙ | |||
1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 | fossil_fatal("cannot compute descendants or ancestors of a date"); } if( mode==0 ){ if( isIsoDate(zOrigin) ) zShift = ",'+1 day'"; } zDate = mprintf("(SELECT julianday(%Q%s, 'utc'))", zOrigin, zShift); } if( mode==0 ) mode = 1; blob_zero(&sql); blob_append(&sql, timeline_query_for_tty(), -1); | > > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > | > > > > > | | | < < < | < < > > | < | | | | | 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 | fossil_fatal("cannot compute descendants or ancestors of a date"); } if( mode==0 ){ if( isIsoDate(zOrigin) ) zShift = ",'+1 day'"; } zDate = mprintf("(SELECT julianday(%Q%s, 'utc'))", zOrigin, zShift); } if( zFilePattern ){ if( zType==0 ){ /* When zFilePattern is specified and type is not specified, only show * file check-ins */ zType="ci"; } file_tree_name(zFilePattern, &treeName, 1); if( fossil_strcmp(blob_str(&treeName), ".")==0 ){ /* When zTreeName refers to g.zLocalRoot, it's like not specifying * zFilePattern. */ zFilePattern = 0; } } if( mode==0 ) mode = 1; blob_zero(&sql); blob_append(&sql, timeline_query_for_tty(), -1); blob_append_sql(&sql, "\n AND event.mtime %s %s", (mode==1 || mode==4) ? "<=" : ">=", zDate /*safe-for-%s*/ ); if( mode==3 || mode==4 ){ db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); if( mode==3 ){ compute_descendants(objid, n); }else{ compute_ancestors(objid, n, 0); } blob_append_sql(&sql, "\n AND blob.rid IN ok"); } if( zType && (zType[0]!='a') ){ blob_append_sql(&sql, "\n AND event.type=%Q ", zType); } if( zFilePattern ){ blob_append(&sql, "\n AND EXISTS(SELECT 1 FROM mlink\n" " WHERE mlink.mid=event.objid\n" " AND mlink.fnid IN ", -1); if( filenames_are_case_sensitive() ){ blob_append_sql(&sql, "(SELECT fnid FROM filename" " WHERE name=%Q" " OR name GLOB '%q/*')", blob_str(&treeName), blob_str(&treeName)); }else{ blob_append_sql(&sql, "(SELECT fnid FROM filename" " WHERE name=%Q COLLATE nocase" " OR lower(name) GLOB lower('%q/*'))", blob_str(&treeName), blob_str(&treeName)); } blob_append(&sql, ")", -1); } blob_append_sql(&sql, "\nORDER BY event.mtime DESC"); if( iOffset>0 ){ /* Don't handle LIMIT here, otherwise print_timeline() * will not determine the end-marker correctly! */ blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset); } db_prepare(&q, "%s", blob_sql_text(&sql)); blob_reset(&sql); print_timeline(&q, n, width, verboseFlag); db_finalize(&q); } /* ** Return one of two things: ** ** ",'localtime'" if the timeline-utc property is set to 0. ** ** "" (empty string) otherwise. */ const char *timeline_utc(){ if( g.fTimeFormat==0 ){ if( db_get_int("timeline-utc", 1) ){ g.fTimeFormat = 1; }else{ g.fTimeFormat = 2; } } if( g.fTimeFormat==1 ){ return ""; }else{ return ",'localtime'"; } } /* ** COMMAND: test-timewarp-list ** ** Usage: %fossil test-timewarp-list ?-v|---verbose? ** ** Display all instances of child check-ins that appear earlier in time ** than their parent. If the -v|--verbose option is provided, both the ** parent and child check-ins and their times are shown. */ void test_timewarp_cmd(void){ Stmt q; int verboseFlag; db_find_and_open_repository(0, 0); verboseFlag = find_option("verbose", "v", 0)!=0; |
︙ | ︙ | |||
1831 1832 1833 1834 1835 1836 1837 | /* ** WEBPAGE: test_timewarps */ void test_timewarp_page(void){ Stmt q; login_check_credentials(); | | > > > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 | /* ** WEBPAGE: test_timewarps */ void test_timewarp_page(void){ Stmt q; login_check_credentials(); if( !g.perm.Read || !g.perm.Hyperlink ){ login_needed(g.anon.Read && g.anon.Hyperlink); return; } style_header("Instances of timewarp"); @ <ul> db_prepare(&q, "SELECT blob.uuid " " FROM plink p, plink c, blob" " WHERE p.cid=c.pid AND p.mtime>c.mtime" " AND blob.rid=c.cid" ); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); @ <li> @ <a href="%R/timeline?dp=%!S(zUuid)&unhide">%S(zUuid)</a> } db_finalize(&q); style_footer(); } |
Changes to src/tkt.c.
︙ | ︙ | |||
59 60 61 62 63 64 65 | for(i=0; i<nField; i++){ if( fossil_strcmp(aField[i].zName, zFieldName)==0 ) return i; } return -1; } /* | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | for(i=0; i<nField; i++){ if( fossil_strcmp(aField[i].zName, zFieldName)==0 ) return i; } return -1; } /* ** Obtain a list of all fields of the TICKET and TICKETCHNG tables. Put them ** in sorted order in aField[]. ** ** The haveTicket and haveTicketChng variables are set to 1 if the TICKET and ** TICKETCHANGE tables exist, respectively. */ static void getAllTicketFields(void){ Stmt q; |
︙ | ︙ | |||
135 136 137 138 139 140 141 | */ static void initializeVariablesFromDb(void){ const char *zName; Stmt q; int i, n, size, j; zName = PD("name","-none-"); | | | > | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | */ static void initializeVariablesFromDb(void){ const char *zName; Stmt q; int i, n, size, j; zName = PD("name","-none-"); db_prepare(&q, "SELECT datetime(tkt_mtime%s) AS tkt_datetime, *" " FROM ticket WHERE tkt_uuid GLOB '%q*'", timeline_utc(), zName); if( db_step(&q)==SQLITE_ROW ){ n = db_column_count(&q); for(i=0; i<n; i++){ const char *zVal = db_column_text(&q, i); const char *zName = db_column_name(&q, i); char *zRevealed = 0; if( zVal==0 ){ |
︙ | ︙ | |||
200 201 202 203 204 205 206 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " "VALUES(%Q, 0)", p->zTicketUuid); tktid = db_last_insert_rowid(); } blob_zero(&sql1); blob_zero(&sql2); blob_zero(&sql3); | | | | | | | | | | | | > | | > > > > > > > > > > > > > > > > > > > > > > > > | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) " "VALUES(%Q, 0)", p->zTicketUuid); tktid = db_last_insert_rowid(); } blob_zero(&sql1); blob_zero(&sql2); blob_zero(&sql3); blob_append_sql(&sql1, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); if( haveTicketCTime ){ blob_append_sql(&sql1, ", tkt_ctime=coalesce(tkt_ctime,:mtime)"); } aUsed = fossil_malloc( nField ); memset(aUsed, 0, nField); for(i=0; i<p->nField; i++){ const char *zName = p->aField[i].zName; const char *zBaseName = zName[0]=='+' ? zName+1 : zName; j = fieldId(zBaseName); if( j<0 ) continue; aUsed[j] = 1; if( aField[j].mUsed & USEDBY_TICKET ){ if( zName[0]=='+' ){ zName++; blob_append_sql(&sql1,", \"%w\"=coalesce(\"%w\",'') || %Q", zName, zName, p->aField[i].zValue); }else{ blob_append_sql(&sql1,", \"%w\"=%Q", zName, p->aField[i].zValue); } } if( aField[j].mUsed & USEDBY_TICKETCHNG ){ blob_append_sql(&sql2, ",\"%w\"", zName); blob_append_sql(&sql3, ",%Q", p->aField[i].zValue); } if( rid>0 ){ wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0); } } blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid); db_prepare(&q, "%s", blob_sql_text(&sql1)); db_bind_double(&q, ":mtime", p->rDate); db_step(&q); db_finalize(&q); blob_reset(&sql1); if( blob_size(&sql2)>0 || haveTicketChngRid ){ int fromTkt = 0; if( haveTicketChngRid ){ blob_append(&sql2, ",tkt_rid", -1); blob_append_sql(&sql3, ",%d", rid); } for(i=0; i<nField; i++){ if( aUsed[i]==0 && (aField[i].mUsed & USEDBY_BOTH)==USEDBY_BOTH ){ const char *z = aField[i].zName; if( z[0]=='+' ) z++; fromTkt = 1; blob_append_sql(&sql2, ",\"%w\"", z); blob_append_sql(&sql3, ",\"%w\"", z); } } if( fromTkt ){ db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" "SELECT %d,:mtime%s FROM ticket WHERE tkt_id=%d", blob_sql_text(&sql2), tktid, blob_sql_text(&sql3), tktid); }else{ db_prepare(&q, "INSERT INTO ticketchng(tkt_id,tkt_mtime%s)" "VALUES(%d,:mtime%s)", blob_sql_text(&sql2), tktid, blob_sql_text(&sql3)); } db_bind_double(&q, ":mtime", p->rDate); db_step(&q); db_finalize(&q); } blob_reset(&sql2); blob_reset(&sql3); fossil_free(aUsed); return tktid; } /* ** Returns non-zero if moderation is required for ticket changes and ticket ** attachments. */ int ticket_need_moderation( int localUser /* Are we being called for a local interactive user? */ ){ /* ** If the FOSSIL_FORCE_TICKET_MODERATION variable is set, *ALL* changes for ** tickets will be required to go through moderation (even those performed ** by the local interactive user via the command line). This can be useful ** for local (or remote) testing of the moderation subsystem and its impact ** on the contents and status of tickets. */ if( fossil_getenv("FOSSIL_FORCE_TICKET_MODERATION")!=0 ){ return 1; } if( localUser ){ return 0; } return g.perm.ModTkt==0 && db_get_boolean("modreq-tkt",0)==1; } /* ** Rebuild an entire entry in the TICKET table */ void ticket_rebuild_entry(const char *zTktUuid){ char *zTag = mprintf("tkt-%s", zTktUuid); int tagid = tag_findid(zTag, 1); Stmt q; Manifest *pTicket; int tktid; int createFlag = 1; fossil_free(zTag); getAllTicketFields(); if( haveTicket==0 ) return; tktid = db_int(0, "SELECT tkt_id FROM ticket WHERE tkt_uuid=%Q", zTktUuid); search_doc_touch('t', tktid, 0); if( haveTicketChng ){ db_multi_exec("DELETE FROM ticketchng WHERE tkt_id=%d;", tktid); } db_multi_exec("DELETE FROM ticket WHERE tkt_id=%d", tktid); tktid = 0; db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid); while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
319 320 321 322 323 324 325 | zConfig = ticket_common_code(); Th_Eval(g.interp, 0, zConfig, -1); } /* ** Create the TH1 interpreter and load the "change" code. */ | | > | | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | zConfig = ticket_common_code(); Th_Eval(g.interp, 0, zConfig, -1); } /* ** Create the TH1 interpreter and load the "change" code. */ int ticket_change(const char *zUuid){ const char *zConfig; Th_FossilInit(TH_INIT_DEFAULT); Th_Store("uuid", zUuid); zConfig = ticket_change_code(); return Th_Eval(g.interp, 0, zConfig, -1); } /* ** Recreate the TICKET and TICKETCHNG tables. */ void ticket_create_table(int separateConnection){ const char *zSql; db_multi_exec( "DROP TABLE IF EXISTS ticket;" "DROP TABLE IF EXISTS ticketchng;" ); zSql = ticket_table_schema(); if( separateConnection ){ db_end_transaction(0); db_init_database(g.zRepositoryName, zSql, 0); }else{ db_multi_exec("%s", zSql/*safe-for-%s*/); } } /* ** Repopulate the TICKET and TICKETCHNG tables from scratch using all ** available ticket artifacts. */ |
︙ | ︙ | |||
399 400 401 402 403 404 405 | static void showAllFields(void){ int i; @ <font color="blue"> @ <p>Database fields:</p><ul> for(i=0; i<nField; i++){ @ <li>aField[%d(i)].zName = "%h(aField[i].zName)"; @ originally = "%h(aField[i].zValue)"; | | | | | | | | | | | | | | | | | | | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | static void showAllFields(void){ int i; @ <font color="blue"> @ <p>Database fields:</p><ul> for(i=0; i<nField; i++){ @ <li>aField[%d(i)].zName = "%h(aField[i].zName)"; @ originally = "%h(aField[i].zValue)"; @ currently = "%h(PD(aField[i].zName,""))"; if( aField[i].zAppend ){ @ zAppend = "%h(aField[i].zAppend)"; } @ mUsed = %d(aField[i].mUsed); } @ </ul></font> } /* ** WEBPAGE: tktview ** URL: tktview?name=UUID ** ** View a ticket. */ void tktview_page(void){ const char *zScript; char *zFullName; const char *zUuid = PD("name",""); login_check_credentials(); if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; } if( g.anon.WrTkt || g.anon.ApndTkt ){ style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T", g.zTop, PD("name","")); } if( g.perm.Hyperlink ){ style_submenu_element("History", "History Of This Ticket", "%s/tkthistory/%T", g.zTop, zUuid); style_submenu_element("Timeline", "Timeline Of This Ticket", "%s/tkttimeline/%T", g.zTop, zUuid); style_submenu_element("Check-ins", "Check-ins Of This Ticket", "%s/tkttimeline/%T?y=ci", g.zTop, zUuid); } if( g.anon.NewTkt ){ style_submenu_element("New Ticket", "Create a new ticket", "%s/tktnew", g.zTop); } if( g.anon.ApndTkt && g.anon.Attach ){ style_submenu_element("Attach", "Add An Attachment", "%s/attachadd?tkt=%T&from=%s/tktview/%t", g.zTop, zUuid, g.zTop, zUuid); } if( P("plaintext") ){ style_submenu_element("Formatted", "Formatted", "%R/tktview/%s", zUuid); }else{ style_submenu_element("Plaintext", "Plaintext", "%R/tktview/%s?plaintext", zUuid); } style_header("View Ticket"); if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1); ticket_init(); initializeVariablesFromCGI(); getAllTicketFields(); initializeVariablesFromDb(); zScript = ticket_viewpage_code(); if( P("showfields")!=0 ) showAllFields(); if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1); Th_Render(zScript); if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); zFullName = db_text(0, "SELECT tkt_uuid FROM ticket" " WHERE tkt_uuid GLOB '%q*'", zUuid); if( zFullName ){ attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>"); } style_footer(); } /* ** TH1 command: append_field FIELD STRING ** ** FIELD is the name of a database column to which we might want ** to append text. STRING is the text to be appended to that ** column. The append does not actually occur until the ** submit_ticket command is run. */ static int appendRemarkCmd( Th_Interp *interp, void *p, int argc, const char **argv, int *argl ){ int idx; if( argc!=3 ){ return Th_WrongNumArgs(interp, "append_field FIELD STRING"); } |
︙ | ︙ | |||
511 512 513 514 515 516 517 | aField[idx].zAppend = mprintf("%.*s", argl[2], argv[2]); return TH_OK; } /* ** Write a ticket into the repository. */ | | > | | > > > | > > | | | | > | 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | aField[idx].zAppend = mprintf("%.*s", argl[2], argv[2]); return TH_OK; } /* ** Write a ticket into the repository. */ static int ticket_put( Blob *pTicket, /* The text of the ticket change record */ const char *zTktId, /* The ticket to which this change is applied */ int needMod /* True if moderation is needed */ ){ int result; int rid = content_put_ex(pTicket, 0, 0, 0, needMod); if( rid==0 ){ fossil_fatal("trouble committing ticket: %s", g.zErrMsg); } if( needMod ){ moderation_table_create(); db_multi_exec( "INSERT INTO modreq(objid, tktid) VALUES(%d,%Q)", rid, zTktId ); }else{ db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); } manifest_crosslink_begin(); result = (manifest_crosslink(rid, pTicket, MC_NONE)==0); assert( blob_is_reset(pTicket) ); if( !result ){ result = manifest_crosslink_end(MC_PERMIT_HOOKS); }else{ manifest_crosslink_end(MC_NONE); } return result; } /* ** Subscript command: submit_ticket ** ** Construct and submit a new ticket artifact. The fields of the artifact ** are the names of the columns in the TICKET table. The content is ** taken from TH variables. If the content is unchanged, the field is ** omitted from the artifact. Fields whose names begin with "private_" ** are concealed using the db_conceal() function. */ static int submitTicketCmd( Th_Interp *interp, void *pUuid, int argc, const char **argv, int *argl ){ char *zDate; const char *zUuid; int i; int nJ = 0; Blob tktchng, cksum; int needMod; login_verify_csrf_secret(); if( !captcha_is_correct() ){ @ <p class="generalError">Error: Incorrect security code.</p> return TH_OK; } zUuid = (const char *)pUuid; |
︙ | ︙ | |||
597 598 599 600 601 602 603 | blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue); } nJ++; } } } if( *(char**)pUuid ){ | | | > > > > | | | | < > | < | | > | | 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 | blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue); } nJ++; } } } if( *(char**)pUuid ){ zUuid = db_text(0, "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%q*'", P("name") ); }else{ zUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); } *(const char**)pUuid = zUuid; blob_appendf(&tktchng, "K %s\n", zUuid); blob_appendf(&tktchng, "U %F\n", login_name()); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); if( nJ==0 ){ blob_reset(&tktchng); return TH_OK; } needMod = ticket_need_moderation(0); if( g.zPath[0]=='d' ){ const char *zNeedMod = needMod ? "required" : "skipped"; /* If called from /debug_tktnew or /debug_tktedit... */ @ <font color="blue"> @ <p>Ticket artifact that would have been submitted:</p> @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> @ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote> @ <hr /></font> return TH_OK; }else{ if( g.thTrace ){ Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" "}<br />\n", blob_str(&tktchng)); } ticket_put(&tktchng, zUuid, needMod); } return ticket_change(zUuid); } /* ** WEBPAGE: tktnew ** WEBPAGE: debug_tktnew ** ** Enter a new ticket. The tktnew_template script in the ticket ** configuration is used. The /tktnew page is the official ticket ** entry page. The /debug_tktnew page is used for debugging the ** tktnew_template in the ticket configuration. /debug_tktnew works ** just like /tktnew except that it does not really save the new ticket ** when you press submit - it just prints the ticket artifact at the ** top of the screen. */ void tktnew_page(void){ const char *zScript; char *zNewUuid = 0; login_check_credentials(); if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; } if( P("cancel") ){ cgi_redirect("home"); } style_header("New Ticket"); ticket_standard_submenu(T_ALL_BUT(T_NEW)); if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1); ticket_init(); initializeVariablesFromCGI(); getAllTicketFields(); initializeVariablesFromDb(); if( g.zPath[0]=='d' ) showAllFields(); form_begin(0, "%R/%s", g.zPath); login_insert_csrf_secret(); if( P("date_override") && g.perm.Setup ){ @ <input type="hidden" name="date_override" value="%h(P("date_override"))"> } zScript = ticket_newpage_code(); Th_Store("login", login_name()); Th_Store("date", db_text(0, "SELECT datetime('now')")); Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zNewUuid, 0); if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1); if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid)); return; |
︙ | ︙ | |||
698 699 700 701 702 703 704 | void tktedit_page(void){ const char *zScript; int nName; const char *zName; int nRec; login_check_credentials(); | | > > > | 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 | void tktedit_page(void){ const char *zScript; int nName; const char *zName; int nRec; login_check_credentials(); if( !g.perm.ApndTkt && !g.perm.WrTkt ){ login_needed(g.anon.ApndTkt || g.anon.WrTkt); return; } zName = P("name"); if( P("cancel") ){ cgi_redirectf("tktview?name=%T", zName); } style_header("Edit Ticket"); if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE || !validate16(zName,nName) ){ |
︙ | ︙ | |||
733 734 735 736 737 738 739 | initializeVariablesFromCGI(); initializeVariablesFromDb(); if( g.zPath[0]=='d' ) showAllFields(); form_begin(0, "%R/%s", g.zPath); @ <input type="hidden" name="name" value="%s(zName)" /> login_insert_csrf_secret(); zScript = ticket_editpage_code(); | | | 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 | initializeVariablesFromCGI(); initializeVariablesFromDb(); if( g.zPath[0]=='d' ) showAllFields(); form_begin(0, "%R/%s", g.zPath); @ <input type="hidden" name="name" value="%s(zName)" /> login_insert_csrf_secret(); zScript = ticket_editpage_code(); Th_Store("login", login_name()); Th_Store("date", db_text(0, "SELECT datetime('now')")); Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1); if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName)); return; |
︙ | ︙ | |||
799 800 801 802 803 804 805 | const char *zUuid; char *zFullUuid; int tagid; char zGlobPattern[50]; const char *zType; login_check_credentials(); | | > > > | | < | 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 | const char *zUuid; char *zFullUuid; int tagid; char zGlobPattern[50]; const char *zType; login_check_credentials(); if( !g.perm.Hyperlink || !g.perm.RdTkt ){ login_needed(g.anon.Hyperlink && g.anon.RdTkt); return; } zUuid = PD("name",""); zType = PD("y","a"); if( zType[0]!='c' ){ style_submenu_element("Check-ins", "Check-ins", "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid); }else{ style_submenu_element("Timeline", "Timeline", "%s/tkttimeline?name=%T", g.zTop, zUuid); } style_submenu_element("History", "History", "%s/tkthistory/%s", g.zTop, zUuid); style_submenu_element("Status", "Status", "%s/info/%s", g.zTop, zUuid); if( zType[0]=='c' ){ zTitle = mprintf("Check-ins Associated With Ticket %h", zUuid); }else{ zTitle = mprintf("Timeline Of Ticket %h", zUuid); } style_header("%z", zTitle); sqlite3_snprintf(6, zGlobPattern, "%s", zUuid); canonical16(zGlobPattern, strlen(zGlobPattern)); tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid); if( tagid==0 ){ @ No such ticket: %h(zUuid) style_footer(); |
︙ | ︙ | |||
852 853 854 855 856 857 858 | " AND '%s' GLOB (target||'*')" " UNION SELECT attachid FROM attachment" " WHERE target=%Q) " "ORDER BY mtime DESC", timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid ); } | | < | | > > > | | | < | | | < < < | | | | | | 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 | " AND '%s' GLOB (target||'*')" " UNION SELECT attachid FROM attachment" " WHERE target=%Q) " "ORDER BY mtime DESC", timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid ); } db_prepare(&q, "%z", zSQL/*safe-for-%s*/); www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH, 0, 0, 0, 0); db_finalize(&q); style_footer(); } /* ** WEBPAGE: tkthistory ** URL: /tkthistory?name=TICKETUUID ** ** Show the complete change history for a single ticket */ void tkthistory_page(void){ Stmt q; char *zTitle; const char *zUuid; int tagid; int nChng = 0; login_check_credentials(); if( !g.perm.Hyperlink || !g.perm.RdTkt ){ login_needed(g.anon.Hyperlink && g.anon.RdTkt); return; } zUuid = PD("name",""); zTitle = mprintf("History Of Ticket %h", zUuid); style_submenu_element("Status", "Status", "%s/info/%s", g.zTop, zUuid); style_submenu_element("Check-ins", "Check-ins", "%s/tkttimeline?name=%s&y=ci", g.zTop, zUuid); style_submenu_element("Timeline", "Timeline", "%s/tkttimeline?name=%s", g.zTop, zUuid); if( P("plaintext")!=0 ){ style_submenu_element("Formatted", "Formatted", "%R/tkthistory/%s", zUuid); }else{ style_submenu_element("Plaintext", "Plaintext", "%R/tkthistory/%s?plaintext", zUuid); } style_header("%z", zTitle); tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid); if( tagid==0 ){ @ No such ticket: %h(zUuid) style_footer(); return; } db_prepare(&q, "SELECT datetime(mtime%s), objid, uuid, NULL, NULL, NULL" " FROM event, blob" " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)" " AND blob.rid=event.objid" " UNION " "SELECT datetime(mtime%s), attachid, uuid, src, filename, user" " FROM attachment, blob" " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)" " AND blob.rid=attachid" " ORDER BY 1", timeline_utc(), tagid, timeline_utc(), tagid ); while( db_step(&q)==SQLITE_ROW ){ Manifest *pTicket; const char *zDate = db_column_text(&q, 0); int rid = db_column_int(&q, 1); const char *zChngUuid = db_column_text(&q, 2); const char *zFile = db_column_text(&q, 4); if( nChng==0 ){ @ <ol> } nChng++; if( zFile!=0 ){ const char *zSrc = db_column_text(&q, 3); const char *zUser = db_column_text(&q, 5); if( zSrc==0 || zSrc[0]==0 ){ @ @ <li><p>Delete attachment "%h(zFile)" }else{ @ @ <li><p>Add attachment @ "%z(href("%R/artifact/%!S",zSrc))%s(zFile)</a>" } @ [%z(href("%R/artifact/%!S",zChngUuid))%S(zChngUuid)</a>] @ (rid %d(rid)) by hyperlink_to_user(zUser,zDate," on"); hyperlink_to_date(zDate, ".</p>"); }else{ pTicket = manifest_get(rid, CFTYPE_TICKET, 0); if( pTicket ){ @ @ <li><p>Ticket change @ [%z(href("%R/artifact/%!S",zChngUuid))%S(zChngUuid)</a>] @ (rid %d(rid)) by hyperlink_to_user(pTicket->zUser,zDate," on"); hyperlink_to_date(zDate, ":"); @ </p> ticket_output_change_artifact(pTicket, "a"); } manifest_destroy(pTicket); |
︙ | ︙ | |||
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 | ** page in the gui is used. This has no effect in JSON mode. ** ** Instead of the report title its possible to use the report ** number. Using the special report number 0 list all columns, ** defined in the ticket table. ** ** %fossil ticket list fields ** ** list all fields, defined for ticket in the fossil repository ** ** %fossil ticket list reports ** ** list all ticket reports, defined in the fossil repository ** ** %fossil ticket set TICKETUUID (FIELD VALUE)+ ?-q|--quote? ** %fossil ticket change TICKETUUID (FIELD VALUE)+ ?-q|--quote? ** ** change ticket identified by TICKETUUID and set the value of | > > | 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 | ** page in the gui is used. This has no effect in JSON mode. ** ** Instead of the report title its possible to use the report ** number. Using the special report number 0 list all columns, ** defined in the ticket table. ** ** %fossil ticket list fields ** %fossil ticket ls fields ** ** list all fields, defined for ticket in the fossil repository ** ** %fossil ticket list reports ** %fossil ticket ls reports ** ** list all ticket reports, defined in the fossil repository ** ** %fossil ticket set TICKETUUID (FIELD VALUE)+ ?-q|--quote? ** %fossil ticket change TICKETUUID (FIELD VALUE)+ ?-q|--quote? ** ** change ticket identified by TICKETUUID and set the value of |
︙ | ︙ | |||
1092 1093 1094 1095 1096 1097 1098 | const char *zTktUuid; /* do some ints, we want to be inside a checkout */ db_find_and_open_repository(0, 0); user_select(); zUser = find_option("user-override",0,1); | | | 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 | const char *zTktUuid; /* do some ints, we want to be inside a checkout */ db_find_and_open_repository(0, 0); user_select(); zUser = find_option("user-override",0,1); if( zUser==0 ) zUser = login_name(); zDate = find_option("date-override",0,1); if( zDate==0 ) zDate = "now"; zDate = date_in_standard_format(zDate); zTktUuid = find_option("uuid-override",0,1); if( zTktUuid && (strlen(zTktUuid)!=40 || !validate16(zTktUuid,40)) ){ fossil_fatal("invalid --uuid-override: must be 40 characters of hex"); } |
︙ | ︙ | |||
1116 1117 1118 1119 1120 1121 1122 | usage("add|change|list|set|show|history"); } n = strlen(g.argv[2]); if( n==1 && g.argv[2][0]=='s' ){ /* set/show cannot be distinguished, so show the usage */ usage("add|change|list|set|show|history"); } | | | 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 | usage("add|change|list|set|show|history"); } n = strlen(g.argv[2]); if( n==1 && g.argv[2][0]=='s' ){ /* set/show cannot be distinguished, so show the usage */ usage("add|change|list|set|show|history"); } if(( strncmp(g.argv[2],"list",n)==0 ) || ( strncmp(g.argv[2],"ls",n)==0 )){ if( g.argc==3 ){ usage("list fields|reports"); }else{ n = strlen(g.argv[3]); if( !strncmp(g.argv[3],"fields",n) ){ /* simply show all field names */ int i; |
︙ | ︙ | |||
1141 1142 1143 1144 1145 1146 1147 | } } }else{ /* add a new ticket or set fields on existing tickets */ tTktShowEncoding tktEncoding; tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; | | | 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 | } } }else{ /* add a new ticket or set fields on existing tickets */ tTktShowEncoding tktEncoding; tktEncoding = find_option("quote","q",0) ? tktFossilize : tktNoTab; if( strncmp(g.argv[2],"show",n)==0 ){ if( g.argc==3 ){ usage("show REPORTNR"); }else{ const char *zRep = 0; const char *zSep = 0; const char *zFilterUuid = 0; |
︙ | ︙ | |||
1176 1177 1178 1179 1180 1181 1182 | eCmd = history; }else{ eCmd = set; } if( g.argc==3 ){ usage("set|change|history TICKETUUID"); } | | | | 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 | eCmd = history; }else{ eCmd = set; } if( g.argc==3 ){ usage("set|change|history TICKETUUID"); } zTktUuid = db_text(0, "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%q*'", g.argv[3] ); if( !zTktUuid ){ fossil_fatal("unknown ticket: '%s'!",g.argv[3]); } i=4; }else if( strncmp(g.argv[2],"add",n)==0 ){ eCmd = add; |
︙ | ︙ | |||
1200 1201 1202 1203 1204 1205 1206 | } /* we just handle history separately here, does not get out */ if( eCmd==history ){ Stmt q; int tagid; | | | | | | | < < | < < | | | | | | | | | | 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 | } /* we just handle history separately here, does not get out */ if( eCmd==history ){ Stmt q; int tagid; if( i != g.argc ){ fossil_fatal("no other parameters expected to %s!",g.argv[2]); } tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'", zTktUuid); if( tagid==0 ){ fossil_fatal("no such ticket %h", zTktUuid); } db_prepare(&q, "SELECT datetime(mtime%s), objid, NULL, NULL, NULL" " FROM event, blob" " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)" " AND blob.rid=event.objid" " UNION " "SELECT datetime(mtime%s), attachid, filename, " " src, user" " FROM attachment, blob" " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)" " AND blob.rid=attachid" " ORDER BY 1 DESC", timeline_utc(), tagid, timeline_utc(), tagid ); while( db_step(&q)==SQLITE_ROW ){ Manifest *pTicket; const char *zDate = db_column_text(&q, 0); int rid = db_column_int(&q, 1); const char *zFile = db_column_text(&q, 2); if( zFile!=0 ){ const char *zSrc = db_column_text(&q, 3); const char *zUser = db_column_text(&q, 4); if( zSrc==0 || zSrc[0]==0 ){ fossil_print("Delete attachment %s\n", zFile); }else{ fossil_print("Add attachment %s\n", zFile); } fossil_print(" by %s on %s\n", zUser, zDate); }else{ pTicket = manifest_get(rid, CFTYPE_TICKET, 0); if( pTicket ){ int i; fossil_print("Ticket Change by %s on %s:\n", pTicket->zUser, zDate); for(i=0; i<pTicket->nField; i++){ Blob val; const char *z; z = pTicket->aField[i].zName; blob_set(&val, pTicket->aField[i].zValue); if( z[0]=='+' ){ fossil_print(" Append to "); z++; }else{ fossil_print(" Change "); } fossil_print("%h: ",z); if( blob_size(&val)>50 || contains_newline(&val)) { fossil_print("\n "); comment_print(blob_str(&val),0,4,-1,g.comFmtFlags); }else{ fossil_print("%s\n",blob_str(&val)); } blob_reset(&val); } } manifest_destroy(pTicket); |
︙ | ︙ | |||
1296 1297 1298 1299 1300 1301 1302 | } zFValue = g.argv[i++]; if( tktEncoding == tktFossilize ){ zFValue=mprintf("%s",zFValue); defossilize(zFValue); } append = (zFName[0] == '+'); | | | | | | | | > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 | } zFValue = g.argv[i++]; if( tktEncoding == tktFossilize ){ zFValue=mprintf("%s",zFValue); defossilize(zFValue); } append = (zFName[0] == '+'); if( append ){ zFName++; } j = fieldId(zFName); if( j == -1 ){ fossil_fatal("unknown field name '%s'!",zFName); }else{ if( append ){ aField[j].zAppend = zFValue; }else{ aField[j].zValue = zFValue; } } } /* now add the needed artifacts to the repository */ blob_zero(&tktchng); /* add the time to the ticket manifest */ blob_appendf(&tktchng, "D %s\n", zDate); /* append defined elements */ for(i=0; i<nField; i++){ char *zValue = 0; char *zPfx; if( aField[i].zAppend && aField[i].zAppend[0] ){ zPfx = " +"; zValue = aField[i].zAppend; }else if( aField[i].zValue && aField[i].zValue[0] ){ zPfx = " "; zValue = aField[i].zValue; }else{ continue; } if( memcmp(aField[i].zName, "private_", 8)==0 ){ zValue = db_conceal(zValue, strlen(zValue)); blob_appendf(&tktchng, "J%s%s %s\n", zPfx, aField[i].zName, zValue); }else{ blob_appendf(&tktchng, "J%s%s %#F\n", zPfx, aField[i].zName, strlen(zValue), zValue); } } blob_appendf(&tktchng, "K %s\n", zTktUuid); blob_appendf(&tktchng, "U %F\n", zUser); md5sum_blob(&tktchng, &cksum); blob_appendf(&tktchng, "Z %b\n", &cksum); if( ticket_put(&tktchng, zTktUuid, ticket_need_moderation(1)) ){ fossil_fatal("%s\n", g.zErrMsg); }else{ fossil_print("ticket %s succeeded for %s\n", (eCmd==set?"set":"add"),zTktUuid); } } } } #if INTERFACE /* Standard submenu items for wiki pages */ #define T_SRCH 0x00001 #define T_REPLIST 0x00002 #define T_NEW 0x00004 #define T_ALL 0x00007 #define T_ALL_BUT(x) (T_ALL&~(x)) #endif /* ** Add some standard submenu elements for ticket screens. */ void ticket_standard_submenu(unsigned int ok){ if( (ok & T_SRCH)!=0 && search_restrict(SRCH_TKT)!=0 ){ style_submenu_element("Search","Search","%R/tktsrch"); } if( (ok & T_REPLIST)!=0 ){ style_submenu_element("Reports","Reports","%R/reportlist"); } if( (ok & T_NEW)!=0 && g.anon.NewTkt ){ style_submenu_element("New","New","%R/tktnew"); } } /* ** WEBPAGE: ticket ** ** This is intended to be the primary "Ticket" page. Render as ** either ticket-search (if search is enabled) or as the ** /reportlist page (if ticket search is disabled). */ void tkt_home_page(void){ login_check_credentials(); if( search_restrict(SRCH_TKT)!=0 ){ tkt_srchpage(); }else{ view_list(); } } /* ** WEBPAGE: tktsrch ** Usage: /tktsrch?s=PATTERN ** ** Full-text search of all current tickets */ void tkt_srchpage(void){ login_check_credentials(); style_header("Ticket Search"); ticket_standard_submenu(T_ALL_BUT(T_SRCH)); search_screen(SRCH_TKT, 0); style_footer(); } |
Changes to src/tktsetup.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | /* ** Main sub-menu for configuring the ticketing system. ** WEBPAGE: tktsetup */ void tktsetup_page(void){ login_check_credentials(); if( !g.perm.Setup ){ | | > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | /* ** Main sub-menu for configuring the ticketing system. ** WEBPAGE: tktsetup */ void tktsetup_page(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_header("Ticket Setup"); @ <table border="0" cellspacing="20"> setup_menu_entry("Table", "tktsetup_tab", "Specify the schema of the \"ticket\" table in the database."); setup_menu_entry("Timeline", "tktsetup_timeline", |
︙ | ︙ | |||
113 114 115 116 117 118 119 | const char *zDesc, /* Description of this field */ char *(*xText)(const char*), /* Validity test or NULL */ void (*xRebuild)(void), /* Run after successful update */ int height /* Height of the edit box */ ){ const char *z; int isSubmit; | | | > | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | const char *zDesc, /* Description of this field */ char *(*xText)(const char*), /* Validity test or NULL */ void (*xRebuild)(void), /* Run after successful update */ int height /* Height of the edit box */ ){ const char *z; int isSubmit; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } if( PB("setup") ){ cgi_redirect("tktsetup"); } isSubmit = P("submit")!=0; z = P("x"); if( z==0 ){ z = db_get(zDbField, (char*)zDfltValue); } |
︙ | ︙ | |||
312 313 314 315 316 317 318 | @ <table cellpadding="5"> @ <tr> @ <td colspan="3"> @ Enter a one-line summary of the ticket:<br /> @ <input type="text" name="title" size="60" value="$<title>" /> @ </td> @ </tr> | | | | | | | | | | | | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | @ <table cellpadding="5"> @ <tr> @ <td colspan="3"> @ Enter a one-line summary of the ticket:<br /> @ <input type="text" name="title" size="60" value="$<title>" /> @ </td> @ </tr> @ @ <tr> @ <td align="right">Type:</td> @ <td align="left"><th1>combobox type $type_choices 1</th1></td> @ <td align="left">What type of ticket is this?</td> @ </tr> @ @ <tr> @ <td align="right">Version:</td> @ <td align="left"> @ <input type="text" name="foundin" size="20" value="$<foundin>" /> @ </td> @ <td align="left">In what version or build number do you observe @ the problem?</td> @ </tr> @ @ <tr> @ <td align="right">Severity:</td> @ <td align="left"><th1>combobox severity $severity_choices 1</th1></td> @ <td align="left">How debilitating is the problem? How badly does the problem @ affect the operation of the product?</td> @ </tr> @ @ <tr> @ <td align="right">EMail:</td> @ <td align="left"> @ <input type="text" name="private_contact" value="$<private_contact>" @ size="30" /> @ </td> @ <td align="left"><u>Not publicly visible</u> @ Used by developers to contact you with questions.</td> @ </tr> @ @ <tr> @ <td colspan="3"> @ Enter a detailed description of the problem. @ For code defects, be sure to provide details on exactly how @ the problem can be reproduced. Provide as much detail as @ possible. Format: @ <th1>combobox mutype {Wiki HTML {Plain Text} {[links only]}} 1</th1> @ <br /> @ <th1>set nline [linecount $comment 50 10]</th1> @ <textarea name="icomment" cols="80" rows="$nline" @ wrap="virtual" class="wikiedit">$<icomment></textarea><br /> @ </tr> @ @ <th1>enable_output [info exists preview]</th1> @ <tr><td colspan="3"> @ Description Preview:<br /><hr /> @ <th1> @ if {$mutype eq "Wiki"} { @ wiki $icomment @ } elseif {$mutype eq "Plain Text"} { @ set r [randhex] @ wiki "<verbatim-$r>[string trimright $icomment]\n</verbatim-$r>" @ } elseif {$mutype eq {[links only]}} { @ set r [randhex] @ wiki "<verbatim-$r links>[string trimright $icomment]\n</verbatim-$r>" @ } else { @ wiki "<nowiki>$icomment\n</nowiki>" @ } @ </th1> @ <hr /></td></tr> @ <th1>enable_output 1</th1> @ @ <tr> @ <td><td align="left"> @ <input type="submit" name="preview" value="Preview" /> @ </td> @ <td align="left">See how the description will appear after formatting.</td> @ </tr> @ @ <th1>enable_output [info exists preview]</th1> @ <tr> @ <td><td align="left"> @ <input type="submit" name="submit" value="Submit" /> @ </td> @ <td align="left">After filling in the information above, press this @ button to create the new ticket</td> @ </tr> @ <th1>enable_output 1</th1> @ @ <tr> @ <td><td align="left"> @ <input type="submit" name="cancel" value="Cancel" /> @ </td> @ <td>Abandon and forget this ticket</td> @ </tr> @ </table> |
︙ | ︙ | |||
433 434 435 436 437 438 439 | ); } static const char zDefaultView[] = @ <table cellpadding="5"> @ <tr><td class="tktDspLabel">Ticket UUID:</td> @ <th1> | > | | | > > > > | > > > > | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | ); } static const char zDefaultView[] = @ <table cellpadding="5"> @ <tr><td class="tktDspLabel">Ticket UUID:</td> @ <th1> @ if {[info exists tkt_uuid]} { @ if {[hascap s]} { @ html "<td class='tktDspValue' colspan='3'>$tkt_uuid " @ html "($tkt_id)</td></tr>\n" @ } else { @ html "<td class='tktDspValue' colspan='3'>$tkt_uuid</td></tr>\n" @ } @ } else { @ if {[hascap s]} { @ html "<td class='tktDspValue' colspan='3'>Deleted " @ html "(0)</td></tr>\n" @ } else { @ html "<td class='tktDspValue' colspan='3'>Deleted</td></tr>\n" @ } @ } @ </th1> @ <tr><td class="tktDspLabel">Title:</td> @ <td class="tktDspValue" colspan="3"> @ $<title> @ </td></tr> @ <tr><td class="tktDspLabel">Status:</td><td class="tktDspValue"> |
︙ | ︙ | |||
463 464 465 466 467 468 469 | @ <tr><td class="tktDspLabel">Subsystem:</td><td class="tktDspValue"> @ $<subsystem> @ </td> @ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue"> @ $<resolution> @ </td></tr> @ <tr><td class="tktDspLabel">Last Modified:</td><td class="tktDspValue"> | > > | > > | | > | | | | | | | | > | | 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | @ <tr><td class="tktDspLabel">Subsystem:</td><td class="tktDspValue"> @ $<subsystem> @ </td> @ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue"> @ $<resolution> @ </td></tr> @ <tr><td class="tktDspLabel">Last Modified:</td><td class="tktDspValue"> @ <th1> @ if {[info exists tkt_datetime]} { @ html $tkt_datetime @ } @ </th1> @ </td> @ <th1>enable_output [hascap e]</th1> @ <td class="tktDspLabel">Contact:</td><td class="tktDspValue"> @ $<private_contact> @ </td> @ <th1>enable_output 1</th1> @ </tr> @ <tr><td class="tktDspLabel">Version Found In:</td> @ <td colspan="3" valign="top" class="tktDspValue"> @ $<foundin> @ </td></tr> @ @ <th1> @ if {[info exists comment]} { @ if {[string length $comment]>10} { @ html { @ <tr><td class="tktDspLabel">Description:</td></tr> @ <tr><td colspan="5" class="tktDspValue"> @ } @ if {[info exists plaintext]} { @ set r [randhex] @ wiki "<verbatim-$r links>\n$comment\n</verbatim-$r>" @ } else { @ wiki $comment @ } @ } @ } @ set seenRow 0 @ set alwaysPlaintext [info exists plaintext] @ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin, @ mimetype as xmimetype, icomment AS xcomment, @ username AS xusername |
︙ | ︙ | |||
575 576 577 578 579 580 581 | @ set preview 1 @ } @ </th1> @ <table cellpadding="5"> @ <tr><td class="tktDspLabel">Title:</td><td> @ <input type="text" name="title" value="$<title>" size="60" /> @ </td></tr> | | | | | | | | | | | | | | | | 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 | @ set preview 1 @ } @ </th1> @ <table cellpadding="5"> @ <tr><td class="tktDspLabel">Title:</td><td> @ <input type="text" name="title" value="$<title>" size="60" /> @ </td></tr> @ @ <tr><td class="tktDspLabel">Status:</td><td> @ <th1>combobox status $status_choices 1</th1> @ </td></tr> @ @ <tr><td class="tktDspLabel">Type:</td><td> @ <th1>combobox type $type_choices 1</th1> @ </td></tr> @ @ <tr><td class="tktDspLabel">Severity:</td><td> @ <th1>combobox severity $severity_choices 1</th1> @ </td></tr> @ @ <tr><td class="tktDspLabel">Priority:</td><td> @ <th1>combobox priority $priority_choices 1</th1> @ </td></tr> @ @ <tr><td class="tktDspLabel">Resolution:</td><td> @ <th1>combobox resolution $resolution_choices 1</th1> @ </td></tr> @ @ <tr><td class="tktDspLabel">Subsystem:</td><td> @ <th1>combobox subsystem $subsystem_choices 1</th1> @ </td></tr> @ @ <th1>enable_output [hascap e]</th1> @ <tr><td class="tktDspLabel">Contact:</td><td> @ <input type="text" name="private_contact" size="40" @ value="$<private_contact>" /> @ </td></tr> @ <th1>enable_output 1</th1> @ @ <tr><td class="tktDspLabel">Version Found In:</td><td> @ <input type="text" name="foundin" size="50" value="$<foundin>" /> @ </td></tr> @ @ <tr><td colspan="2"> @ Append Remark with format @ <th1>combobox mutype {Wiki HTML {Plain Text} {[links only]}} 1</th1> @ from @ <input type="text" name="username" value="$<username>" size="30" />:<br /> @ <textarea name="icomment" cols="80" rows="15" @ wrap="virtual" class="wikiedit">$<icomment></textarea> @ </td></tr> @ @ <th1>enable_output [info exists preview]</th1> @ <tr><td colspan="2"> @ Description Preview:<br><hr> @ <th1> @ if {$mutype eq "Wiki"} { @ wiki $icomment @ } elseif {$mutype eq "Plain Text"} { @ set r [randhex] @ wiki "<verbatim-$r>\n[string trimright $icomment]\n</verbatim-$r>" @ } elseif {$mutype eq {[links only]}} { @ set r [randhex] @ wiki "<verbatim-$r links>\n[string trimright $icomment]</verbatim-$r>" @ } else { @ wiki "<nowiki>\n[string trimright $icomment]\n</nowiki>" @ } @ </th1> @ <hr> @ </td></tr> @ <th1>enable_output 1</th1> @ @ <tr> @ <td align="right"> @ <input type="submit" name="preview" value="Preview" /> @ </td> @ <td align="left">See how the description will appear after formatting.</td> @ </tr> @ @ <th1>enable_output [info exists preview]</th1> @ <tr> @ <td align="right"> @ <input type="submit" name="submit" value="Submit" /> @ </td> @ <td align="left">Apply the changes shown above</td> @ </tr> @ <th1>enable_output 1</th1> @ @ <tr> @ <td align="right"> @ <input type="submit" name="cancel" value="Cancel" /> @ </td> @ <td>Abandon this edit</td> @ </tr> @ @ </table> ; /* ** Return the code used to generate the edit ticket page */ const char *ticket_editpage_code(void){ |
︙ | ︙ | |||
696 697 698 699 700 701 702 | } /* ** The default report list page */ static const char zDefaultReportList[] = @ <th1> | | | | | | | | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 | } /* ** The default report list page */ static const char zDefaultReportList[] = @ <th1> @ if {[anoncap n]} { @ html "<p>Enter a new ticket:</p>" @ html "<ul><li><a href='tktnew'>New ticket</a></li></ul>" @ } @ </th1> @ @ <p>Choose a report format from the following list:</p> @ <ol> @ <th1>html $report_items</th1> @ </ol> @ @ <th1> @ if {[anoncap t q]} { @ html "<p>Other options:</p>\n<ul>\n" @ if {[anoncap t]} { @ html "<li><a href='rptnew'>New report format</a></li>\n" @ } @ if {[anoncap q]} { @ html "<li><a href='modreq'>Tend to pending moderation requests</a></li>\n" @ } @ } @ </th1> ; /* |
︙ | ︙ | |||
748 749 750 751 752 753 754 | 40 ); } /* ** The default template ticket report format: */ | | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 | 40 ); } /* ** The default template ticket report format: */ static char zDefaultReport[] = @ SELECT @ CASE WHEN status IN ('Open','Verified') THEN '#f2dcdc' @ WHEN status='Review' THEN '#e8e8e8' @ WHEN status='Fixed' THEN '#cfe8bd' @ WHEN status='Tested' THEN '#bde5d6' @ WHEN status='Deferred' THEN '#cacae5' @ ELSE '#c8c8c8' END AS 'bgcolor', |
︙ | ︙ | |||
797 798 799 800 801 802 803 | 20 ); } /* ** The default template ticket key: */ | | | 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 | 20 ); } /* ** The default template ticket key: */ static const char zDefaultKey[] = @ #ffffff Key: @ #f2dcdc Active @ #e8e8e8 Review @ #cfe8bd Fixed @ #bde5d6 Tested @ #cacae5 Deferred @ #c8c8c8 Closed |
︙ | ︙ | |||
841 842 843 844 845 846 847 | /* ** WEBPAGE: tktsetup_timeline */ void tktsetup_timeline_page(void){ login_check_credentials(); if( !g.perm.Setup ){ | | > | 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 | /* ** WEBPAGE: tktsetup_timeline */ void tktsetup_timeline_page(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } if( P("setup") ){ cgi_redirect("tktsetup"); } style_header("Ticket Display On Timelines"); db_begin_transaction(); |
︙ | ︙ | |||
878 879 880 881 882 883 884 | @ <p> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="setup" value="Cancel" /> @ </p> @ </div></form> db_end_transaction(0); style_footer(); | | | 896 897 898 899 900 901 902 903 904 | @ <p> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="setup" value="Cancel" /> @ </p> @ </div></form> db_end_transaction(0); style_footer(); } |
Changes to src/translate.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** | | | | | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** SYNOPSIS: ** ** Input lines that begin with the "@" character are translated into ** either cgi_printf() statements or string literals and the ** translated code is written on standard output. ** ** The problem this program is attempt to solve is as follows: When ** writing CGI programs in C, we typically want to output a lot of HTML ** text to standard output. In pure C code, this involves doing a ** printf() with a big string containing all that text. But we have ** to insert special codes (ex: \n and \") for many common characters, ** which interferes with the readability of the HTML. ** ** This tool allows us to put raw HTML, without the special codes, in ** the middle of a C program. This program then translates the text ** into standard C by inserting all necessary backslashes and other ** punctuation. ** ** Enhancement #1: ** ** If the last non-whitespace character prior to the first "@" of a ** @-block is "=" or "," then the @-block is a string literal initializer ** rather than text that is to be output via cgi_printf(). Render it ** as such. ** ** Enhancement #2: ** ** Comments of the form: "/* @-comment: CC" cause CC to become a ** comment character for the @-substitution. Typical values for CC are ** "--" (for SQL text) or "#" (for Tcl script) or "//" (for C++ code). ** Lines of subsequent @-blocks that begin with CC are omitted from the ** output. ** */ #include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> /* |
︙ | ︙ | |||
120 121 122 123 124 125 126 | i++; if( isspace(zLine[i]) ){ i++; } indent = i - 2; if( indent<0 ) indent = 0; omitline = 0; for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){ | | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | i++; if( isspace(zLine[i]) ){ i++; } indent = i - 2; if( indent<0 ) indent = 0; omitline = 0; for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){ omitline = 1; break; } if( zLine[i]=='"' || zLine[i]=='\\' ){ zOut[j++] = '\\'; } zOut[j++] = zLine[i]; } while( j>0 && isspace(zOut[j-1]) ){ j--; } zOut[j] = 0; if( j<=0 && omitline ){ fprintf(out,"\n"); }else{ fprintf(out,"%*s\"%s\\n\"\n",indent, "", zOut); } }else{ /* Otherwise (if the last non-whitespace was not '=') then generate ** a cgi_printf() statement whose format is the text following the '@'. ** Substrings of the form "%C(...)" (where C is any sequence of ** characters other than \000 and '(') will put "%C" in the ** format and add the "(...)" as an argument to the cgi_printf call. */ int indent; int nC; char c; i++; |
︙ | ︙ | |||
172 173 174 175 176 177 178 | zOut[j] = 0; if( !inPrint ){ fprintf(out,"%*scgi_printf(\"%s\\n\"",indent-2,"", zOut); inPrint = 1; }else{ fprintf(out,"\n%*s\"%s\\n\"",indent+5, "", zOut); } | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | zOut[j] = 0; if( !inPrint ){ fprintf(out,"%*scgi_printf(\"%s\\n\"",indent-2,"", zOut); inPrint = 1; }else{ fprintf(out,"\n%*s\"%s\\n\"",indent+5, "", zOut); } } } } int main(int argc, char **argv){ if( argc==2 ){ char *arg; FILE *in = fopen(argv[1], "r"); |
︙ | ︙ |
Changes to src/undo.c.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file implements the undo/redo functionality. */ #include "config.h" #include "undo.h" |
︙ | ︙ | |||
43 44 45 46 47 48 49 | int new_exe; int new_link; int old_link; Blob current; Blob new; zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname); old_link = db_column_int(&q, 3); | < > | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | int new_exe; int new_link; int old_link; Blob current; Blob new; zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname); old_link = db_column_int(&q, 3); new_exists = file_wd_size(zFullname)>=0; new_link = file_wd_islink(0); if( new_exists ){ if( new_link ){ blob_read_link(¤t, zFullname); }else{ blob_read_from_file(¤t, zFullname); } new_exe = file_wd_isexe(0); }else{ blob_zero(¤t); new_exe = 0; } blob_zero(&new); old_exists = db_column_int(&q, 1); old_exe = db_column_int(&q, 2); |
︙ | ︙ | |||
84 85 86 87 88 89 90 | }else{ fossil_print("DELETE %s\n", zPathname); file_delete(zFullname); } blob_reset(&new); free(zFullname); db_finalize(&q); | | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | }else{ fossil_print("DELETE %s\n", zPathname); file_delete(zFullname); } blob_reset(&new); free(zFullname); db_finalize(&q); db_prepare(&q, "UPDATE undo SET content=:c, existsflag=%d, isExe=%d, isLink=%d," " redoflag=NOT redoflag" " WHERE pathname=%Q", new_exists, new_exe, new_link, zPathname ); if( new_exists ){ db_bind_blob(&q, ":c", ¤t); |
︙ | ︙ | |||
125 126 127 128 129 130 131 | /* ** Undo or redo all undoable or redoable changes. */ static void undo_all(int redoFlag){ int ucid; int ncid; | < | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | /* ** Undo or redo all undoable or redoable changes. */ static void undo_all(int redoFlag){ int ucid; int ncid; undo_all_filesystem(redoFlag); db_multi_exec( "CREATE TEMP TABLE undo_vfile_2 AS SELECT * FROM vfile;" "DELETE FROM vfile;" "INSERT INTO vfile SELECT * FROM undo_vfile;" "DELETE FROM undo_vfile;" "INSERT INTO undo_vfile SELECT * FROM undo_vfile_2;" "DROP TABLE undo_vfile_2;" "CREATE TEMP TABLE undo_vmerge_2 AS SELECT * FROM vmerge;" "DELETE FROM vmerge;" "INSERT INTO vmerge SELECT * FROM undo_vmerge;" "DELETE FROM undo_vmerge;" "INSERT INTO undo_vmerge SELECT * FROM undo_vmerge_2;" "DROP TABLE undo_vmerge_2;" ); if( db_table_exists("localdb", "undo_stash") ){ if( redoFlag ){ db_multi_exec( "DELETE FROM stash WHERE stashid IN (SELECT stashid FROM undo_stash);" "DELETE FROM stashfile" " WHERE stashid NOT IN (SELECT stashid FROM stash);" ); }else{ |
︙ | ︙ | |||
172 173 174 175 176 177 178 | static const char zSql[] = @ DROP TABLE IF EXISTS undo; @ DROP TABLE IF EXISTS undo_vfile; @ DROP TABLE IF EXISTS undo_vmerge; @ DROP TABLE IF EXISTS undo_stash; @ DROP TABLE IF EXISTS undo_stashfile; ; | | | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | static const char zSql[] = @ DROP TABLE IF EXISTS undo; @ DROP TABLE IF EXISTS undo_vfile; @ DROP TABLE IF EXISTS undo_vmerge; @ DROP TABLE IF EXISTS undo_stash; @ DROP TABLE IF EXISTS undo_stashfile; ; db_multi_exec(zSql /*works-like:""*/); db_lset_int("undo_available", 0); db_lset_int("undo_checkout", 0); } /* ** The following variable stores the original command-line of the ** command that is a candidate to be undone. |
︙ | ︙ | |||
216 217 218 219 220 221 222 | /* ** Begin capturing a snapshot that can be undone. */ void undo_begin(void){ int cid; const char *zDb = db_name("localdb"); | | | | | | | | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | /* ** Begin capturing a snapshot that can be undone. */ void undo_begin(void){ int cid; const char *zDb = db_name("localdb"); static const char zSql[] = @ CREATE TABLE "%w".undo( @ pathname TEXT UNIQUE, -- Name of the file @ redoflag BOOLEAN, -- 0 for undoable. 1 for redoable @ existsflag BOOLEAN, -- True if the file exists @ isExe BOOLEAN, -- True if the file is executable @ isLink BOOLEAN, -- True if the file is symlink @ content BLOB -- Saved content @ ); @ CREATE TABLE "%w".undo_vfile AS SELECT * FROM vfile; @ CREATE TABLE "%w".undo_vmerge AS SELECT * FROM vmerge; ; if( undoDisable ) return; undo_reset(); db_multi_exec(zSql/*works-like:"%w,%w,%w"*/, zDb, zDb, zDb); cid = db_lget_int("checkout", 0); db_lset_int("undo_checkout", cid); db_lset_int("undo_available", 1); db_lset("undo_cmdline", undoCmd); undoActive = 1; } /* ** Permanently disable undo */ void undo_disable(void){ undoDisable = 1; } /* ** This flag is true if one or more files have changed and have been |
︙ | ︙ | |||
278 279 280 281 282 283 284 | "INSERT OR IGNORE INTO" " undo(pathname,redoflag,existsflag,isExe,isLink,content)" " VALUES(%Q,0,%d,%d,%d,:c)", zPathname, existsFlag, file_wd_isexe(zFullname), isLink ); if( existsFlag ){ if( isLink ){ | | | | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | "INSERT OR IGNORE INTO" " undo(pathname,redoflag,existsflag,isExe,isLink,content)" " VALUES(%Q,0,%d,%d,%d,:c)", zPathname, existsFlag, file_wd_isexe(zFullname), isLink ); if( existsFlag ){ if( isLink ){ blob_read_link(&content, zFullname); }else{ blob_read_from_file(&content, zFullname); } db_bind_blob(&q, ":c", &content); } free(zFullname); db_step(&q); db_finalize(&q); if( existsFlag ){ blob_reset(&content); } undoNeedRollback = 1; } /* ** Make the current state of stashid undoable. */ void undo_save_stash(int stashid){ const char *zDb = db_name("localdb"); db_multi_exec( "CREATE TABLE IF NOT EXISTS \"%w\".undo_stash" " AS SELECT * FROM stash WHERE 0;" "INSERT INTO undo_stash" " SELECT * FROM stash WHERE stashid=%d;", zDb, stashid ); db_multi_exec( "CREATE TABLE IF NOT EXISTS \"%w\".undo_stashfile" " AS SELECT * FROM stashfile WHERE 0;" "INSERT INTO undo_stashfile" " SELECT * FROM stashfile WHERE stashid=%d;", zDb, stashid ); } |
︙ | ︙ | |||
362 363 364 365 366 367 368 | ** ** (1) fossil update (5) fossil stash apply ** (2) fossil merge (6) fossil stash drop ** (3) fossil revert (7) fossil stash goto ** (4) fossil stash pop ** ** If FILENAME is specified then restore the content of the named | | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | ** ** (1) fossil update (5) fossil stash apply ** (2) fossil merge (6) fossil stash drop ** (3) fossil revert (7) fossil stash goto ** (4) fossil stash pop ** ** If FILENAME is specified then restore the content of the named ** file(s) but otherwise leave the update or merge or revert in effect. ** The redo command undoes the effect of the most recent undo. ** ** If the -n|--dry-run option is present, no changes are made and instead ** the undo or redo command explains what actions the undo or redo would ** have done had the -n|--dry-run been omitted. ** ** A single level of undo/redo is supported. The undo/redo stack |
︙ | ︙ | |||
409 410 411 412 413 414 415 | ); while( db_step(&q)==SQLITE_ROW ){ if( nChng==0 ){ fossil_print("The following file changes would occur if the " "command above is %sne:\n\n", zCmd); } nChng++; | | | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | ); while( db_step(&q)==SQLITE_ROW ){ if( nChng==0 ){ fossil_print("The following file changes would occur if the " "command above is %sne:\n\n", zCmd); } nChng++; fossil_print("%s %s\n", db_column_int(&q,0) ? "UPDATE" : "DELETE", db_column_text(&q, 1) ); } db_finalize(&q); if( nChng==0 ){ fossil_print("No file changes would occur with this undo/redo.\n"); |
︙ | ︙ |
Changes to src/unicode.c.
︙ | ︙ | |||
29 30 31 32 33 34 35 | ** is less than zero. */ int unicode_isalnum(int c){ /* Each unsigned integer in the following array corresponds to a contiguous ** range of unicode codepoints that are not either letters or numbers (i.e. ** codepoints for which this function should return 0). ** | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | > | > | | | > > > > | | | | | | | | | | | < | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | ** is less than zero. */ int unicode_isalnum(int c){ /* Each unsigned integer in the following array corresponds to a contiguous ** range of unicode codepoints that are not either letters or numbers (i.e. ** codepoints for which this function should return 0). ** ** The most significant 22 bits in each 32-bit value contain the first ** codepoint in the range. The least significant 10 bits are used to store ** the size of the range (always at least 1). In other words, the value ** ((C<<22) + N) represents a range of N codepoints starting with codepoint ** C. It is not possible to represent a range larger than 1023 codepoints ** using this format. */ static const unsigned int aEntry[] = { 0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07, 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01, 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401, 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01, 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163403, 0x00164437, 0x0017CC02, 0x0018001D, 0x00187802, 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F, 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401, 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804, 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403, 0x00217801, 0x00239020, 0x0024E803, 0x0024F812, 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001, 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802, 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805, 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401, 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03, 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807, 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001, 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01, 0x002FCC08, 0x00300004, 0x0030F807, 0x00311803, 0x00312804, 0x00315402, 0x00318802, 0x0031FC01, 0x00320403, 0x0032F001, 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802, 0x00340403, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01, 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06, 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007, 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006, 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417, 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14, 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07, 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01, 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001, 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802, 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F, 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002, 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802, 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006, 0x006AC00F, 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D, 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802, 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x0073E002, 0x00770036, 0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403, 0x007FB403, 0x007FF402, 0x00800065, 0x0081980A, 0x0081E805, 0x00822805, 0x0082801E, 0x00834021, 0x00840002, 0x00840C04, 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401, 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005, 0x00852804, 0x00853C01, 0x0086426B, 0x00900027, 0x0091000B, 0x0092704E, 0x00940276, 0x009E53E0, 0x00ADD820, 0x00AE6022, 0x00AEF40C, 0x00AF2808, 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001, 0x00B5FC01, 0x00B7804F, 0x00B8C013, 0x00BA001A, 0x00BA6C59, 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807, 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01, 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E, 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100, 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10, 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402, 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804, 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012, 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004, 0x02A6CC1B, 0x02A77802, 0x02A79401, 0x02A8A40E, 0x02A90C01, 0x02A93002, 0x02A97004, 0x02A9DC03, 0x02A9EC03, 0x02AAC001, 0x02AAC803, 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07, 0x02ABD402, 0x02AD6C01, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02, 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F8800E, 0x03F8C023, 0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003, 0x0404DC09, 0x0405E411, 0x04063001, 0x0406400C, 0x04068001, 0x0407402E, 0x040B8001, 0x040DD805, 0x040E7C01, 0x040F4001, 0x0415BC01, 0x04215C01, 0x0421DC02, 0x04247C01, 0x0424FC01, 0x04280403, 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009, 0x0429FC01, 0x042B2001, 0x042B9402, 0x042BC007, 0x042CE407, 0x042E6404, 0x04400003, 0x0440E016, 0x0441FC04, 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x0445CC03, 0x04460003, 0x0446CC0E, 0x04471404, 0x04473401, 0x0448B012, 0x044B7C0C, 0x044C0403, 0x044CF001, 0x044CF807, 0x044D1C02, 0x044D2C03, 0x044D5C01, 0x044D8802, 0x044D9807, 0x044DC005, 0x0452C014, 0x04531801, 0x0456BC07, 0x0456E012, 0x0458C014, 0x045AAC0D, 0x0491C005, 0x05A9B802, 0x05ABC006, 0x05ACC010, 0x05AD1002, 0x05BD442E, 0x05BE3C04, 0x06F27008, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401, 0x075F0C01, 0x07A34007, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F, 0x07C2C40F, 0x07C3040F, 0x07C34425, 0x07C4401F, 0x07C4C03C, 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002, 0x07CC002D, 0x07CCC04E, 0x07CE004F, 0x07CF5024, 0x07D000FF, 0x07D4004B, 0x07D5402A, 0x07D5EC29, 0x07D6949E, 0x07D9148B, 0x07DB800D, 0x07DBC004, 0x07DC0074, 0x07DE0055, 0x07E0000C, 0x07E04038, 0x07E1400A, 0x07E18028, 0x07E2401E, 0x38000401, 0x38008060, 0x380400F0, }; static const unsigned int aAscii[4] = { 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001, }; if( c<128 ){ return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 ); |
︙ | ︙ | |||
158 159 160 161 162 163 164 | ** in the ASCII range with a diacritic added, return the codepoint ** of the ASCII letter only. For example, if passed 235 - "LATIN ** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER ** E"). The resuls of passing a codepoint that corresponds to an ** uppercase letter are undefined. */ static int unicode_remove_diacritic(int c){ | | | | | | | | | | | | | | | | | | | | | | | | | < > | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | ** in the ASCII range with a diacritic added, return the codepoint ** of the ASCII letter only. For example, if passed 235 - "LATIN ** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER ** E"). The resuls of passing a codepoint that corresponds to an ** uppercase letter are undefined. */ static int unicode_remove_diacritic(int c){ static const unsigned short aDia[] = { 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286, 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732, 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336, 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928, 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504, 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726, 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122, 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536, 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730, 62924, 63050, 63082, 63274, 63390, }; static const char aChar[] = { '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c', 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r', 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o', 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r', 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h', 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't', 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a', 'e', 'i', 'o', 'u', 'y', }; unsigned int key = (((unsigned int)c)<<3) | 0x00000007; int iRes = 0; int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1; int iLo = 0; while( iHi>=iLo ){ int iTest = (iHi + iLo) / 2; if( key >= aDia[iTest] ){ iRes = iTest; iLo = iTest+1; }else{ iHi = iTest-1; } } assert( key>=aDia[iRes] ); return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]); } /* ** Return true if the argument interpreted as a unicode codepoint ** is a diacritical modifier character. */ int unicode_is_diacritic(int c){ |
︙ | ︙ | |||
251 252 253 254 255 256 257 | static const struct TableEntry { unsigned short iCode; unsigned char flags; unsigned char nRange; } aEntry[] = { {65, 14, 26}, {181, 64, 1}, {192, 14, 23}, {216, 14, 7}, {256, 1, 48}, {306, 1, 6}, | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | | | | | | > | | | | | | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | static const struct TableEntry { unsigned short iCode; unsigned char flags; unsigned char nRange; } aEntry[] = { {65, 14, 26}, {181, 64, 1}, {192, 14, 23}, {216, 14, 7}, {256, 1, 48}, {306, 1, 6}, {313, 1, 16}, {330, 1, 46}, {376, 126, 1}, {377, 1, 6}, {383, 114, 1}, {385, 50, 1}, {386, 1, 4}, {390, 44, 1}, {391, 0, 1}, {393, 42, 2}, {395, 0, 1}, {398, 32, 1}, {399, 38, 1}, {400, 40, 1}, {401, 0, 1}, {403, 42, 1}, {404, 46, 1}, {406, 52, 1}, {407, 48, 1}, {408, 0, 1}, {412, 52, 1}, {413, 54, 1}, {415, 56, 1}, {416, 1, 6}, {422, 60, 1}, {423, 0, 1}, {425, 60, 1}, {428, 0, 1}, {430, 60, 1}, {431, 0, 1}, {433, 58, 2}, {435, 1, 4}, {439, 62, 1}, {440, 0, 1}, {444, 0, 1}, {452, 2, 1}, {453, 0, 1}, {455, 2, 1}, {456, 0, 1}, {458, 2, 1}, {459, 1, 18}, {478, 1, 18}, {497, 2, 1}, {498, 1, 4}, {502, 132, 1}, {503, 144, 1}, {504, 1, 40}, {544, 120, 1}, {546, 1, 18}, {570, 70, 1}, {571, 0, 1}, {573, 118, 1}, {574, 68, 1}, {577, 0, 1}, {579, 116, 1}, {580, 28, 1}, {581, 30, 1}, {582, 1, 10}, {837, 36, 1}, {880, 1, 4}, {886, 0, 1}, {895, 36, 1}, {902, 18, 1}, {904, 16, 3}, {908, 26, 1}, {910, 24, 2}, {913, 14, 17}, {931, 14, 9}, {962, 0, 1}, {975, 4, 1}, {976, 150, 1}, {977, 152, 1}, {981, 156, 1}, {982, 154, 1}, {984, 1, 24}, {1008, 146, 1}, {1009, 148, 1}, {1012, 140, 1}, {1013, 138, 1}, {1015, 0, 1}, {1017, 162, 1}, {1018, 0, 1}, {1021, 120, 3}, {1024, 34, 16}, {1040, 14, 32}, {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1}, {1217, 1, 14}, {1232, 1, 96}, {1329, 22, 38}, {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1}, {7680, 1, 150}, {7835, 142, 1}, {7838, 106, 1}, {7840, 1, 96}, {7944, 160, 8}, {7960, 160, 6}, {7976, 160, 8}, {7992, 160, 8}, {8008, 160, 6}, {8025, 161, 8}, {8040, 160, 8}, {8072, 160, 8}, {8088, 160, 8}, {8104, 160, 8}, {8120, 160, 2}, {8122, 136, 2}, {8124, 158, 1}, {8126, 110, 1}, {8136, 134, 4}, {8140, 158, 1}, {8152, 160, 2}, {8154, 130, 2}, {8168, 160, 2}, {8170, 128, 2}, {8172, 162, 1}, {8184, 122, 2}, {8186, 124, 2}, {8188, 158, 1}, {8486, 108, 1}, {8490, 102, 1}, {8491, 104, 1}, {8498, 12, 1}, {8544, 8, 16}, {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47}, {11360, 0, 1}, {11362, 98, 1}, {11363, 112, 1}, {11364, 100, 1}, {11367, 1, 6}, {11373, 94, 1}, {11374, 96, 1}, {11375, 90, 1}, {11376, 92, 1}, {11378, 0, 1}, {11381, 0, 1}, {11390, 88, 2}, {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1}, {42560, 1, 46}, {42624, 1, 28}, {42786, 1, 14}, {42802, 1, 62}, {42873, 1, 4}, {42877, 86, 1}, {42878, 1, 10}, {42891, 0, 1}, {42893, 82, 1}, {42896, 1, 4}, {42902, 1, 20}, {42922, 76, 1}, {42923, 72, 1}, {42924, 74, 1}, {42925, 78, 1}, {42928, 84, 1}, {42929, 80, 1}, {65313, 14, 26}, }; static const unsigned short aiOff[] = { 1, 2, 8, 15, 16, 26, 28, 32, 37, 38, 40, 48, 63, 64, 69, 71, 79, 80, 116, 202, 203, 205, 206, 207, 209, 210, 211, 213, 214, 217, 218, 219, 775, 7264, 10792, 10795, 23217, 23221, 23228, 23231, 23254, 23256, 23278, 30204, 54721, 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274, 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406, 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462, 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511, 65514, 65521, 65527, 65528, 65529, }; int ret = c; assert( c>=0 ); assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 ); |
︙ | ︙ | |||
352 353 354 355 356 357 358 | ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF; assert( ret>0 ); } } if( bRemoveDiacritic ) ret = unicode_remove_diacritic(ret); } | | > > > | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF; assert( ret>0 ); } } if( bRemoveDiacritic ) ret = unicode_remove_diacritic(ret); } else if( c>=66560 && c<66600 ){ ret = c + 40; } else if( c>=71840 && c<71872 ){ ret = c + 32; } return ret; } |
Changes to src/update.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | /* This variable is set if we are doing an internal update. It is clear ** when running the "update" command. */ static int internalUpdate = 0; static int internalConflictCnt = 0; /* | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | /* This variable is set if we are doing an internal update. It is clear ** when running the "update" command. */ static int internalUpdate = 0; static int internalConflictCnt = 0; /* ** Do an update to version vid. ** ** Start an undo session but do not terminate it. Do not autosync. */ int update_to(int vid){ int savedArgc; char **savedArgv; char *newArgv[3]; |
︙ | ︙ | |||
60 61 62 63 64 65 66 | } /* ** COMMAND: update ** ** Usage: %fossil update ?OPTIONS? ?VERSION? ?FILES...? ** | | | | | | | | > | | | | > > > > > | | | | | > > > > > > > > > > > > > > > > | > > > < < < | > > | | > | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | } /* ** COMMAND: update ** ** Usage: %fossil update ?OPTIONS? ?VERSION? ?FILES...? ** ** Change the version of the current checkout to VERSION. Any ** uncommitted changes are retained and applied to the new checkout. ** ** The VERSION argument can be a specific version or tag or branch ** name. If the VERSION argument is omitted, then the leaf of the ** subtree that begins at the current version is used, if there is ** only a single leaf. VERSION can also be "current" to select the ** leaf of the current version or "latest" to select the most recent ** check-in. ** ** If one or more FILES are listed after the VERSION then only the ** named files are candidates to be updated, and any updates to them ** will be treated as edits to the current version. Using a directory ** name for one of the FILES arguments is the same as using every ** subdirectory and file beneath that directory. ** ** If FILES is omitted, all files in the current checkout are subject ** to being updated and the version of the current checkout is changed ** to VERSION. Any uncommitted changes are retained and applied to the ** new checkout. ** ** The -n or --dry-run option causes this command to do a "dry run". ** It prints out what would have happened but does not actually make ** any changes to the current checkout or the repository. ** ** The -v or --verbose option prints status information about ** unchanged files in addition to those file that actually do change. ** ** Options: ** --case-sensitive <BOOL> override case-sensitive setting ** --debug print debug information on stdout ** --latest acceptable in place of VERSION, update to latest version ** --force-missing force update if missing content after sync ** -n|--dry-run If given, display instead of run actions ** -v|--verbose print status information about all files ** -W|--width <num> Width of lines (default is to auto-detect). Must be >20 ** or 0 (= no limit, resulting in a single line per entry). ** ** See also: revert */ void update_cmd(void){ int vid; /* Current version */ int tid=0; /* Target version - version we are changing to */ Stmt q; int latestFlag; /* --latest. Pick the latest version if true */ int dryRunFlag; /* -n or --dry-run. Do a dry run */ int verboseFlag; /* -v or --verbose. Output extra information */ int forceMissingFlag; /* --force-missing. Continue if missing content */ int debugFlag; /* --debug option */ int setmtimeFlag; /* --setmtime. Set mtimes on files */ int nChng; /* Number of file renames */ int *aChng; /* Array of file renames */ int i; /* Loop counter */ int nConflict = 0; /* Number of merge conflicts */ int nOverwrite = 0; /* Number of unmanaged files overwritten */ int nUpdate = 0; /* Number of changes of any kind */ int width; /* Width of printed comment lines */ Stmt mtimeXfer; /* Statement to transfer mtimes */ const char *zWidth; /* Width option string value */ if( !internalUpdate ){ undo_capture_command_line(); url_proxy_options(); } zWidth = find_option("width","W",1); if( zWidth ){ width = atoi(zWidth); if( (width!=0) && (width<=20) ){ fossil_fatal("-W|--width value must be >20 or 0"); } }else{ width = -1; } latestFlag = find_option("latest",0, 0)!=0; dryRunFlag = find_option("dry-run","n",0)!=0; if( !dryRunFlag ){ dryRunFlag = find_option("nochange",0,0)!=0; /* deprecated */ } verboseFlag = find_option("verbose","v",0)!=0; forceMissingFlag = find_option("force-missing",0,0)!=0; debugFlag = find_option("debug",0,0)!=0; setmtimeFlag = find_option("setmtime",0,0)!=0; /* We should be done with options.. */ verify_all_options(); db_must_be_within_tree(); vid = db_lget_int("checkout", 0); user_select(); if( !dryRunFlag && !internalUpdate ){ if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, db_get_int("autosync-tries", 1)) ){ fossil_fatal("Cannot proceed with update"); } } /* Create any empty directories now, as well as after the update, ** so changes in settings are reflected now */ if( !dryRunFlag ) ensure_empty_dirs_created(); if( internalUpdate ){ tid = internalUpdate; }else if( g.argc>=3 ){ |
︙ | ︙ | |||
155 156 157 158 159 160 161 | if( tid==0 ){ fossil_fatal("no such version: %s", g.argv[2]); }else if( !is_a_version(tid) ){ fossil_fatal("no such version: %s", g.argv[2]); } } } | | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | if( tid==0 ){ fossil_fatal("no such version: %s", g.argv[2]); }else if( !is_a_version(tid) ){ fossil_fatal("no such version: %s", g.argv[2]); } } } /* If no VERSION is specified on the command-line, then look for a ** descendent of the current version. If there are multiple descendants, ** look for one from the same branch as the current version. If there ** are still multiple descendants, show them all and refuse to update ** until the user selects one. */ if( tid==0 ){ |
︙ | ︙ | |||
180 181 182 183 184 185 186 | " WHERE leaves.rid=tagxref.rid AND tagxref.tagid=%d" " AND tagxref.value==(SELECT value FROM tagxref" " WHERE tagid=%d AND rid=%d))", TAG_BRANCH, TAG_BRANCH, vid ); if( db_int(0, "SELECT count(*) FROM leaves")>1 ){ compute_leaves(vid, closeCode); | | | | | | > > | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | " WHERE leaves.rid=tagxref.rid AND tagxref.tagid=%d" " AND tagxref.value==(SELECT value FROM tagxref" " WHERE tagid=%d AND rid=%d))", TAG_BRANCH, TAG_BRANCH, vid ); if( db_int(0, "SELECT count(*) FROM leaves")>1 ){ compute_leaves(vid, closeCode); db_prepare(&q, "%s " " AND event.objid IN leaves" " ORDER BY event.mtime DESC", timeline_query_for_tty() ); print_timeline(&q, -100, width, 0); db_finalize(&q); fossil_fatal("Multiple descendants"); } } tid = db_int(0, "SELECT rid FROM leaves, event" " WHERE event.objid=leaves.rid" " ORDER BY event.mtime DESC"); if( tid==0 ) tid = vid; } if( tid==0 ){ return; } db_begin_transaction(); vfile_check_signature(vid, CKSIG_ENOTFILE); if( !dryRunFlag && !internalUpdate ) undo_begin(); if( load_vfile_from_rid(tid) && !forceMissingFlag ){ fossil_fatal("missing content, unable to update"); }; /* ** The record.fn field is used to match files against each other. The ** FV table contains one row for each each unique filename in ** in the current checkout, the pivot, and the version being merged. */ db_multi_exec( |
︙ | ︙ | |||
241 242 243 244 245 246 247 | " FROM vfile WHERE vid=%d", vid ); /* Compute file name changes on V->T. Record name changes in files that ** have changed locally. */ | > | | | | | | | | | | | > | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | " FROM vfile WHERE vid=%d", vid ); /* Compute file name changes on V->T. Record name changes in files that ** have changed locally. */ if( vid ){ find_filename_changes(vid, tid, 1, &nChng, &aChng, debugFlag ? "V->T": 0); if( nChng ){ for(i=0; i<nChng; i++){ db_multi_exec( "UPDATE fv" " SET fnt=(SELECT name FROM filename WHERE fnid=%d)" " WHERE fn=(SELECT name FROM filename WHERE fnid=%d) AND chnged", aChng[i*2+1], aChng[i*2] ); } fossil_free(aChng); } } /* Add files found in the target version T but missing from the current ** version V. */ db_multi_exec( "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged)" |
︙ | ︙ | |||
326 327 328 329 330 331 332 | blob_zero(&sql); blob_append(&sql, "DELETE FROM fv WHERE ", -1); zSep = ""; for(i=3; i<g.argc; i++){ file_tree_name(g.argv[i], &treename, 1); if( file_wd_isdir(g.argv[i])==1 ){ if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ){ | | > | > | | | | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 | blob_zero(&sql); blob_append(&sql, "DELETE FROM fv WHERE ", -1); zSep = ""; for(i=3; i<g.argc; i++){ file_tree_name(g.argv[i], &treename, 1); if( file_wd_isdir(g.argv[i])==1 ){ if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ){ blob_append_sql(&sql, "%sfn NOT GLOB '%q/*' ", zSep /*safe-for-%s*/, blob_str(&treename)); }else{ blob_reset(&sql); break; } }else{ blob_append_sql(&sql, "%sfn<>%Q ", zSep /*safe-for-%s*/, blob_str(&treename)); } zSep = "AND "; blob_reset(&treename); } db_multi_exec("%s", blob_sql_text(&sql)); blob_reset(&sql); } /* ** Alter the content of the checkout so that it conforms with the ** target */ db_prepare(&q, "SELECT fn, idv, ridv, idt, ridt, chnged, fnt," " isexe, islinkv, islinkt, deleted FROM fv ORDER BY 1" ); db_prepare(&mtimeXfer, "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)" " WHERE id=:idt" ); assert( g.zLocalRoot!=0 ); assert( strlen(g.zLocalRoot)>0 ); assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' ); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); /* The filename from root */ int idv = db_column_int(&q, 1); /* VFILE entry for current */ int ridv = db_column_int(&q, 2); /* RecordID for current */ int idt = db_column_int(&q, 3); /* VFILE entry for target */ int ridt = db_column_int(&q, 4); /* RecordID for target */ |
︙ | ︙ | |||
437 438 439 440 441 442 443 | if( nameChng ){ fossil_print("MERGE %s -> %s\n", zName, zNewName); }else{ fossil_print("MERGE %s\n", zName); } if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){ fossil_print("***** Cannot merge symlink %s\n", zNewName); | | | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | if( nameChng ){ fossil_print("MERGE %s -> %s\n", zName, zNewName); }else{ fossil_print("MERGE %s\n", zName); } if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){ fossil_print("***** Cannot merge symlink %s\n", zNewName); nConflict++; }else{ unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; undo_save(zName); content_get(ridt, &t); content_get(ridv, &v); rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags); if( rc>=0 ){ |
︙ | ︙ | |||
511 512 513 514 515 516 517 | case -2: zLabel = "backout merge"; break; } fossil_warning("uncommitted %s against %S.", zLabel, db_column_text(&q, 0)); nMerge++; } db_finalize(&q); | | | | 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 | case -2: zLabel = "backout merge"; break; } fossil_warning("uncommitted %s against %S.", zLabel, db_column_text(&q, 0)); nMerge++; } db_finalize(&q); if( nConflict ){ if( internalUpdate ){ internalConflictCnt = nConflict; nConflict = 0; }else{ fossil_warning("WARNING: %d merge conflicts", nConflict); } } if( nOverwrite ){ fossil_warning("WARNING: %d unmanaged files were overwritten", nOverwrite); } if( nMerge ){ fossil_warning("WARNING: %d uncommitted prior merges", nMerge); } } /* ** Clean up the mid and pid VFILE entries. Then commit the changes. */ if( dryRunFlag ){ db_end_transaction(1); /* With --dry-run, rollback changes */ }else{ ensure_empty_dirs_created(); |
︙ | ︙ | |||
582 583 584 585 586 587 588 | const char *zDir = blob_str(&dirName); /* Make full pathname of the directory */ Blob path; const char *zPath; blob_zero(&path); blob_appendf(&path, "%s/%s", g.zLocalRoot, zDir); | | | | | | | > > | | | | | 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 | const char *zDir = blob_str(&dirName); /* Make full pathname of the directory */ Blob path; const char *zPath; blob_zero(&path); blob_appendf(&path, "%s/%s", g.zLocalRoot, zDir); zPath = blob_str(&path); /* Handle various cases of existence of the directory */ switch( file_wd_isdir(zPath) ){ case 0: { /* doesn't exist */ if( file_mkdir(zPath, 0)!=0 ) { fossil_warning("couldn't create directory %s as " "required by empty-dirs setting", zDir); } break; } case 1: { /* exists, and is a directory */ /* do nothing - required directory exists already */ break; } case 2: { /* exists, but isn't a directory */ fossil_warning("file %s found, but a directory is required " "by empty-dirs setting", zDir); } } blob_reset(&path); } } } /* ** Get the contents of a file within the check-in "revision". If ** revision==NULL then get the file content for the current checkout. */ int historical_version_of_file( const char *revision, /* The check-in containing the file */ const char *file, /* Full treename of the file */ Blob *content, /* Put the content here */ int *pIsLink, /* Set to true if file is link. */ int *pIsExe, /* Set to true if file is executable */ int *pIsBin, /* Set to true if file is binary */ int errCode /* Error code if file not found. Panic if 0. */ ){ Manifest *pManifest; ManifestFile *pFile; int rid=0; if( revision ){ rid = name_to_typed_rid(revision,"ci"); }else if( !g.localOpen ){ rid = name_to_typed_rid(db_get("main-branch","trunk"),"ci"); }else{ rid = db_lget_int("checkout", 0); } if( !is_a_version(rid) ){ if( errCode>0 ) return errCode; fossil_fatal("no such check-in: %s", revision); } pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); if( pManifest ){ pFile = manifest_file_find(pManifest, file); if( pFile ){ int rc; rid = uuid_to_rid(pFile->zUuid, 0); if( pIsExe ) *pIsExe = ( manifest_file_mperm(pFile)==PERM_EXE ); if( pIsLink ) *pIsLink = ( manifest_file_mperm(pFile)==PERM_LNK ); manifest_destroy(pManifest); rc = content_get(rid, content); if( rc && pIsBin ){ *pIsBin = looks_like_binary(content); } return rc; } manifest_destroy(pManifest); if( errCode<=0 ){ fossil_fatal("file %s does not exist in check-in: %s", file, revision); } }else if( errCode<=0 ){ if( revision==0 ){ revision = db_text("current", "SELECT uuid FROM blob WHERE rid=%d", rid); } fossil_fatal("could not parse manifest for check-in: %s", revision); } return errCode; } /* ** COMMAND: revert |
︙ | ︙ | |||
693 694 695 696 697 698 699 | const char *zFile; const char *zRevision; Blob record; int i; int errCode; Stmt q; | | | | 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 | const char *zFile; const char *zRevision; Blob record; int i; int errCode; Stmt q; undo_capture_command_line(); zRevision = find_option("revision", "r", 1); verify_all_options(); if( g.argc<2 ){ usage("?OPTIONS? [FILE] ..."); } if( zRevision && g.argc<3 ){ fossil_fatal("the --revision option does not work for the entire tree"); } db_must_be_within_tree(); |
︙ | ︙ | |||
718 719 720 721 722 723 724 | zFile = mprintf("%/", g.argv[i]); file_tree_name(zFile, &fname, 1); db_multi_exec( "REPLACE INTO torevert VALUES(%B);" "INSERT OR IGNORE INTO torevert" " SELECT pathname" " FROM vfile" | | < < < < | | < > > > > | | | | < | 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 | zFile = mprintf("%/", g.argv[i]); file_tree_name(zFile, &fname, 1); db_multi_exec( "REPLACE INTO torevert VALUES(%B);" "INSERT OR IGNORE INTO torevert" " SELECT pathname" " FROM vfile" " WHERE origname=%B;", &fname, &fname ); blob_reset(&fname); } }else{ int vid; vid = db_lget_int("checkout", 0); vfile_check_signature(vid, 0); db_multi_exec( "DELETE FROM vmerge;" "INSERT OR IGNORE INTO torevert " " SELECT pathname" " FROM vfile " " WHERE chnged OR deleted OR rid=0 OR pathname!=origname;" ); } db_multi_exec( "INSERT OR IGNORE INTO torevert" " SELECT origname" " FROM vfile" " WHERE origname!=pathname AND pathname IN (SELECT name FROM torevert);" ); blob_zero(&record); db_prepare(&q, "SELECT name FROM torevert"); if( zRevision==0 ){ int vid = db_lget_int("checkout", 0); zRevision = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); } while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
767 768 769 770 771 772 773 | fossil_print("UNMANAGE: %s\n", zFile); }else{ undo_save(zFile); file_delete(zFull); fossil_print("DELETE: %s\n", zFile); } db_multi_exec( | | | | | 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | fossil_print("UNMANAGE: %s\n", zFile); }else{ undo_save(zFile); file_delete(zFull); fossil_print("DELETE: %s\n", zFile); } db_multi_exec( "UPDATE OR REPLACE vfile" " SET pathname=origname, origname=NULL" " WHERE pathname=%Q AND origname!=pathname;" "DELETE FROM vfile WHERE pathname=%Q", zFile, zFile ); }else{ sqlite3_int64 mtime; undo_save(zFile); if( file_wd_size(zFull)>=0 && (isLink || file_wd_islink(0)) ){ file_delete(zFull); } if( isLink ){ symlink_create(blob_str(&record), zFull); }else{ blob_write_to_file(&record, zFull); } |
︙ | ︙ |
Changes to src/url.c.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ** ******************************************************************************* ** ** This file contains code for parsing URLs that appear on the command-line */ #include "config.h" #include "url.h" #if INTERFACE /* ** Flags for url_parse() */ #define URL_PROMPT_PW 0x001 /* Prompt for password if needed */ #define URL_REMEMBER 0x002 /* Remember the url for later reuse */ #define URL_ASK_REMEMBER_PW 0x004 /* Ask whether to remember prompted pw */ #define URL_REMEMBER_PW 0x008 /* Should remember pw */ #define URL_PROMPTED 0x010 /* Prompted for PW already */ #endif /* INTERFACE */ /* ** Convert a string to lower-case. */ static void url_tolower(char *z){ while( *z ){ *z = fossil_tolower(*z); z++; } } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | < < < < < < < < | > > > > < < | > | < > | > | | | | | | | < | | | | | > > > | | | < < < > > | > > | > > > | > > > > > | | | | | | | | < < < < < < > > > > > > | | | | | | | | | | < < < < < < | | | | | | | | | > | | | | > | | | > | | | | | | > > | > | | < < | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 | ** ******************************************************************************* ** ** This file contains code for parsing URLs that appear on the command-line */ #include "config.h" #include "url.h" #include <stdio.h> #ifdef _WIN32 #include <io.h> #ifndef isatty #define isatty(d) _isatty(d) #endif #ifndef fileno #define fileno(s) _fileno(s) #endif #endif #if INTERFACE /* ** Flags for url_parse() */ #define URL_PROMPT_PW 0x001 /* Prompt for password if needed */ #define URL_REMEMBER 0x002 /* Remember the url for later reuse */ #define URL_ASK_REMEMBER_PW 0x004 /* Ask whether to remember prompted pw */ #define URL_REMEMBER_PW 0x008 /* Should remember pw */ #define URL_PROMPTED 0x010 /* Prompted for PW already */ /* ** The URL related data used with this subsystem. */ struct UrlData { int isFile; /* True if a "file:" url */ int isHttps; /* True if a "https:" url */ int isSsh; /* True if an "ssh:" url */ char *name; /* Hostname for http: or filename for file: */ char *hostname; /* The HOST: parameter on http headers */ char *protocol; /* "http" or "https" */ int port; /* TCP port number for http: or https: */ int dfltPort; /* The default port for the given protocol */ char *path; /* Pathname for http: */ char *user; /* User id for http: */ char *passwd; /* Password for http: */ char *canonical; /* Canonical representation of the URL */ char *proxyAuth; /* Proxy-Authorizer: string */ char *fossil; /* The fossil query parameter on ssh: */ unsigned flags; /* Boolean flags controlling URL processing */ int useProxy; /* Used to remember that a proxy is in use */ char *proxyUrlPath; int proxyOrigPort; /* Tunneled port number for https through proxy */ }; #endif /* INTERFACE */ /* ** Convert a string to lower-case. */ static void url_tolower(char *z){ while( *z ){ *z = fossil_tolower(*z); z++; } } /* ** Parse the given URL. Populate members of the provided UrlData structure ** as follows: ** ** isFile True if FILE: ** isHttps True if HTTPS: ** isSsh True if SSH: ** protocol "http" or "https" or "file" ** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: ** port TCP port number for HTTP or HTTPS. ** dfltPort Default TCP port number (80 or 443). ** path Path name for HTTP or HTTPS. ** user Userid. ** passwd Password. ** hostname HOST:PORT or just HOST if port is the default. ** canonical The URL in canonical form, omitting the password ** */ void url_parse_local( const char *zUrl, unsigned int urlFlags, UrlData *pUrlData ){ int i, j, c; char *zFile = 0; if( zUrl==0 ){ zUrl = db_get("last-sync-url", 0); if( zUrl==0 ) return; if( pUrlData->passwd==0 ){ pUrlData->passwd = unobscure(db_get("last-sync-pw", 0)); } } if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 || strncmp(zUrl, "ssh://", 6)==0 ){ int iStart; char *zLogin; char *zExe; char cQuerySep = '?'; pUrlData->isFile = 0; pUrlData->useProxy = 0; if( zUrl[4]=='s' ){ pUrlData->isHttps = 1; pUrlData->protocol = "https"; pUrlData->dfltPort = 443; iStart = 8; }else if( zUrl[0]=='s' ){ pUrlData->isSsh = 1; pUrlData->protocol = "ssh"; pUrlData->dfltPort = 22; pUrlData->fossil = "fossil"; iStart = 6; }else{ pUrlData->isHttps = 0; pUrlData->protocol = "http"; pUrlData->dfltPort = 80; iStart = 7; } for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} if( c=='@' ){ /* Parse up the user-id and password */ for(j=iStart; j<i && zUrl[j]!=':'; j++){} pUrlData->user = mprintf("%.*s", j-iStart, &zUrl[iStart]); dehttpize(pUrlData->user); if( j<i ){ if( ( urlFlags & URL_REMEMBER ) && pUrlData->isSsh==0 ){ urlFlags |= URL_ASK_REMEMBER_PW; } pUrlData->passwd = mprintf("%.*s", i-j-1, &zUrl[j+1]); dehttpize(pUrlData->passwd); } if( pUrlData->isSsh ){ urlFlags &= ~URL_ASK_REMEMBER_PW; } zLogin = mprintf("%t@", pUrlData->user); for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); i = j; }else{ int inSquare = 0; int n; for(i=iStart; (c=zUrl[i])!=0 && c!='/' && (inSquare || c!=':'); i++){ if( c=='[' ) inSquare = 1; if( c==']' ) inSquare = 0; } pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); n = strlen(pUrlData->name); if( pUrlData->name[0]=='[' && n>2 && pUrlData->name[n-1]==']' ){ pUrlData->name++; pUrlData->name[n-2] = 0; } zLogin = mprintf(""); } url_tolower(pUrlData->name); if( c==':' ){ pUrlData->port = 0; i++; while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ pUrlData->port = pUrlData->port*10 + c - '0'; i++; } pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port); }else{ pUrlData->port = pUrlData->dfltPort; pUrlData->hostname = pUrlData->name; } dehttpize(pUrlData->name); pUrlData->path = mprintf("%s", &zUrl[i]); for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} if( pUrlData->path[i] ){ pUrlData->path[i] = 0; i++; } zExe = mprintf(""); while( pUrlData->path[i]!=0 ){ char *zName, *zValue; zName = &pUrlData->path[i]; zValue = zName; while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; } if( pUrlData->path[i]=='=' ){ pUrlData->path[i] = 0; i++; zValue = &pUrlData->path[i]; while( pUrlData->path[i] && pUrlData->path[i]!='&' ){ i++; } } if( pUrlData->path[i] ){ pUrlData->path[i] = 0; i++; } if( fossil_strcmp(zName,"fossil")==0 ){ pUrlData->fossil = zValue; dehttpize(pUrlData->fossil); zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil); cQuerySep = '&'; } } dehttpize(pUrlData->path); if( pUrlData->dfltPort==pUrlData->port ){ pUrlData->canonical = mprintf( "%s://%s%T%T%s", pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe ); }else{ pUrlData->canonical = mprintf( "%s://%s%T:%d%T%s", pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port, pUrlData->path, zExe ); } if( pUrlData->isSsh && pUrlData->path[1] ) pUrlData->path++; free(zLogin); }else if( strncmp(zUrl, "file:", 5)==0 ){ pUrlData->isFile = 1; if( zUrl[5]=='/' && zUrl[6]=='/' ){ i = 7; }else{ i = 5; } zFile = mprintf("%s", &zUrl[i]); }else if( file_isfile(zUrl) ){ pUrlData->isFile = 1; zFile = mprintf("%s", zUrl); }else if( file_isdir(zUrl)==1 ){ zFile = mprintf("%s/FOSSIL", zUrl); if( file_isfile(zFile) ){ pUrlData->isFile = 1; }else{ free(zFile); zFile = 0; fossil_fatal("unknown repository: %s", zUrl); } }else{ fossil_fatal("unknown repository: %s", zUrl); } if( urlFlags ) pUrlData->flags = urlFlags; if( pUrlData->isFile ){ Blob cfile; dehttpize(zFile); file_canonical_name(zFile, &cfile, 0); free(zFile); zFile = 0; pUrlData->protocol = "file"; pUrlData->path = ""; pUrlData->name = mprintf("%b", &cfile); pUrlData->canonical = mprintf("file://%T", pUrlData->name); blob_reset(&cfile); }else if( pUrlData->user!=0 && pUrlData->passwd==0 && (urlFlags & URL_PROMPT_PW) ){ url_prompt_for_password_local(pUrlData); }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ if( isatty(fileno(stdin)) ){ if( save_password_prompt(pUrlData->passwd) ){ pUrlData->flags = urlFlags |= URL_REMEMBER_PW; }else{ pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; } } } } /* ** Parse the given URL, which describes a sync server. Populate variables ** in the global "g" structure as follows: ** ** g.url.isFile True if FILE: ** g.url.isHttps True if HTTPS: ** g.url.isSsh True if SSH: ** g.url.protocol "http" or "https" or "file" ** g.url.name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: ** g.url.port TCP port number for HTTP or HTTPS. ** g.url.dfltPort Default TCP port number (80 or 443). ** g.url.path Path name for HTTP or HTTPS. ** g.url.user Userid. ** g.url.passwd Password. ** g.url.hostname HOST:PORT or just HOST if port is the default. ** g.url.canonical The URL in canonical form, omitting the password ** ** HTTP url format as follows (HTTPS is the same with a different scheme): ** ** http://userid:password@host:port/path ** ** SSH url format is: ** ** ssh://userid@host:port/path?fossil=path/to/fossil.exe ** */ void url_parse(const char *zUrl, unsigned int urlFlags){ url_parse_local(zUrl, urlFlags, &g.url); } /* ** COMMAND: test-urlparser ** ** Usage: %fossil test-urlparser URL ?options? ** ** --remember Store results in last-sync-url |
︙ | ︙ | |||
262 263 264 265 266 267 268 | } if( find_option("prompt-pw",0,0) ) fg |= URL_PROMPT_PW; if( g.argc!=3 && g.argc!=4 ){ usage("URL"); } url_parse(g.argv[2], fg); for(i=0; i<2; i++){ | | | | | | | | | | | | | | | | | 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | } if( find_option("prompt-pw",0,0) ) fg |= URL_PROMPT_PW; if( g.argc!=3 && g.argc!=4 ){ usage("URL"); } url_parse(g.argv[2], fg); for(i=0; i<2; i++){ fossil_print("g.url.isFile = %d\n", g.url.isFile); fossil_print("g.url.isHttps = %d\n", g.url.isHttps); fossil_print("g.url.isSsh = %d\n", g.url.isSsh); fossil_print("g.url.protocol = %s\n", g.url.protocol); fossil_print("g.url.name = %s\n", g.url.name); fossil_print("g.url.port = %d\n", g.url.port); fossil_print("g.url.dfltPort = %d\n", g.url.dfltPort); fossil_print("g.url.hostname = %s\n", g.url.hostname); fossil_print("g.url.path = %s\n", g.url.path); fossil_print("g.url.user = %s\n", g.url.user); fossil_print("g.url.passwd = %s\n", g.url.passwd); fossil_print("g.url.canonical = %s\n", g.url.canonical); fossil_print("g.url.fossil = %s\n", g.url.fossil); fossil_print("g.url.flags = 0x%02x\n", g.url.flags); if( g.url.isFile || g.url.isSsh ) break; if( i==0 ){ fossil_print("********\n"); url_enable_proxy("Using proxy: "); } } } |
︙ | ︙ | |||
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | ** This also happens to be a convenient function to use to look for ** the --nosync option that will temporarily disable the "autosync" ** feature. */ void url_proxy_options(void){ zProxyOpt = find_option("proxy", 0, 1); if( find_option("nosync",0,0) ) g.fNoSync = 1; } /* ** If the "proxy" setting is defined, then change the URL settings ** (initialized by a prior call to url_parse()) so that the HTTP ** header will be appropriate for the proxy and so that the TCP/IP ** connection will be opened to the proxy rather than to the server. ** ** If zMsg is not NULL and a proxy is used, then print zMsg followed ** by the canonical name of the proxy (with userid and password suppressed). */ void url_enable_proxy(const char *zMsg){ const char *zProxy; zProxy = zProxyOpt; if( zProxy==0 ){ zProxy = db_get("proxy", 0); | > | | | | > | | > > | | | | | | | | | | | > > > > | | > | | > < > > | > > > > | | | > | > > > > > > > > > > > > > > > > | | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 | ** This also happens to be a convenient function to use to look for ** the --nosync option that will temporarily disable the "autosync" ** feature. */ void url_proxy_options(void){ zProxyOpt = find_option("proxy", 0, 1); if( find_option("nosync",0,0) ) g.fNoSync = 1; if( find_option("ipv4",0,0) ) g.fIPv4 = 1; } /* ** If the "proxy" setting is defined, then change the URL settings ** (initialized by a prior call to url_parse()) so that the HTTP ** header will be appropriate for the proxy and so that the TCP/IP ** connection will be opened to the proxy rather than to the server. ** ** If zMsg is not NULL and a proxy is used, then print zMsg followed ** by the canonical name of the proxy (with userid and password suppressed). */ void url_enable_proxy(const char *zMsg){ const char *zProxy; zProxy = zProxyOpt; if( zProxy==0 ){ zProxy = db_get("proxy", 0); if( zProxy==0 || zProxy[0]==0 || is_false(zProxy) ){ zProxy = fossil_getenv("http_proxy"); } } if( zProxy && zProxy[0] && !is_false(zProxy) && !g.url.isSsh && !g.url.isFile ){ char *zOriginalUrl = g.url.canonical; char *zOriginalHost = g.url.hostname; int fOriginalIsHttps = g.url.isHttps; char *zOriginalUser = g.url.user; char *zOriginalPasswd = g.url.passwd; char *zOriginalUrlPath = g.url.path; int iOriginalPort = g.url.port; unsigned uOriginalFlags = g.url.flags; g.url.user = 0; g.url.passwd = ""; url_parse(zProxy, 0); if( zMsg ) fossil_print("%s%s\n", zMsg, g.url.canonical); g.url.path = zOriginalUrl; g.url.hostname = zOriginalHost; if( g.url.user ){ char *zCredentials1 = mprintf("%s:%s", g.url.user, g.url.passwd); char *zCredentials2 = encode64(zCredentials1, -1); g.url.proxyAuth = mprintf("Basic %z", zCredentials2); free(zCredentials1); } g.url.user = zOriginalUser; g.url.passwd = zOriginalPasswd; g.url.isHttps = fOriginalIsHttps; g.url.useProxy = 1; g.url.proxyUrlPath = zOriginalUrlPath; g.url.proxyOrigPort = iOriginalPort; g.url.flags = uOriginalFlags; } } #if INTERFACE /* ** An instance of this object is used to build a URL with query parameters. */ struct HQuery { Blob url; /* The URL */ const char *zBase; /* The base URL */ int nParam; /* Number of parameters. */ int nAlloc; /* Number of allocated slots */ const char **azName; /* Parameter names */ const char **azValue; /* Parameter values */ }; #endif /* ** Initialize the URL object. */ void url_initialize(HQuery *p, const char *zBase){ memset(p, 0, sizeof(*p)); blob_zero(&p->url); p->zBase = zBase; } /* ** Resets the given URL object, deallocating any memory ** it uses. */ void url_reset(HQuery *p){ blob_reset(&p->url); fossil_free(p->azName); fossil_free(p->azValue); url_initialize(p, p->zBase); } /* ** Add a fixed parameter to an HQuery. Or remove the parameters if zValue==0. */ void url_add_parameter(HQuery *p, const char *zName, const char *zValue){ int i; for(i=0; i<p->nParam; i++){ if( fossil_strcmp(p->azName[i],zName)==0 ){ if( zValue==0 ){ p->nParam--; p->azValue[i] = p->azValue[p->nParam]; p->azName[i] = p->azName[p->nParam]; }else{ p->azValue[i] = zValue; } return; } } assert( i==p->nParam ); if( zValue==0 ) return; if( i>=p->nAlloc ){ p->nAlloc = p->nAlloc*2 + 10; p->azName = fossil_realloc(p->azName, sizeof(p->azName[0])*p->nAlloc); p->azValue = fossil_realloc(p->azValue, sizeof(p->azValue[0])*p->nAlloc); } p->azName[i] = zName; p->azValue[i] = zValue; p->nParam++; } /* ** Render the URL with a parameter override. ** ** Returned memory is transient and is overwritten on the next call to this ** routine for the same HQuery, or until the HQuery object is destroyed. */ char *url_render( HQuery *p, /* Base URL */ const char *zName1, /* First override */ const char *zValue1, /* First override value */ const char *zName2, /* Second override */ const char *zValue2 /* Second override value */ ){ const char *zSep = "?"; int i; blob_reset(&p->url); blob_appendf(&p->url, "%s/%s", g.zTop, p->zBase); for(i=0; i<p->nParam; i++){ const char *z = p->azValue[i]; if( zName1 && fossil_strcmp(zName1,p->azName[i])==0 ){ zName1 = 0; z = zValue1; |
︙ | ︙ | |||
433 434 435 436 437 438 439 | blob_appendf(&p->url, "%s%s", zSep, zName2); if( zValue2[0] ) blob_appendf(&p->url, "=%T", zValue2); } return blob_str(&p->url); } /* | | > | | | | | < < < < < < < | > | | < | < < < | < < | > | > > > > > > > > | > | | | | < > | | | < | 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 | blob_appendf(&p->url, "%s%s", zSep, zName2); if( zValue2[0] ) blob_appendf(&p->url, "=%T", zValue2); } return blob_str(&p->url); } /* ** Prompt the user for the password that corresponds to the "user" member of ** the provided UrlData structure. Store the result into the "passwd" member ** of the provided UrlData structure. */ void url_prompt_for_password_local(UrlData *pUrlData){ if( pUrlData->isSsh || pUrlData->isFile ) return; if( isatty(fileno(stdin)) && (pUrlData->flags & URL_PROMPT_PW)!=0 && (pUrlData->flags & URL_PROMPTED)==0 ){ pUrlData->flags |= URL_PROMPTED; pUrlData->passwd = prompt_for_user_password(pUrlData->user); if( pUrlData->passwd[0] && (pUrlData->flags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 ){ if( save_password_prompt(pUrlData->passwd) ){ pUrlData->flags |= URL_REMEMBER_PW; }else{ pUrlData->flags &= ~URL_REMEMBER_PW; } } }else{ fossil_fatal("missing or incorrect password for user \"%s\"", pUrlData->user); } } /* ** Prompt the user for the password for g.url.user. Store the result ** in g.url.passwd. */ void url_prompt_for_password(void){ url_prompt_for_password_local(&g.url); } /* ** Remember the URL and password if requested. */ void url_remember(void){ if( g.url.flags & URL_REMEMBER ){ db_set("last-sync-url", g.url.canonical, 0); if( g.url.user!=0 && g.url.passwd!=0 && ( g.url.flags & URL_REMEMBER_PW ) ){ db_set("last-sync-pw", obscure(g.url.passwd), 0); } } } /* Preemptively prompt for a password if a username is given in the ** URL but no password. */ void url_get_password_if_needed(void){ if( (g.url.user && g.url.user[0]) && (g.url.passwd==0 || g.url.passwd[0]==0) && isatty(fileno(stdin)) ){ url_prompt_for_password(); } } |
Changes to src/user.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | while( fossil_isspace(*z) ){ z++; } for(i=0; z[i]; i++){ if( z[i]=='\r' || z[i]=='\n' ){ while( i>0 && fossil_isspace(z[i-1]) ){ i--; } z[i] = 0; break; } | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | while( fossil_isspace(*z) ){ z++; } for(i=0; z[i]; i++){ if( z[i]=='\r' || z[i]=='\n' ){ while( i>0 && fossil_isspace(z[i-1]) ){ i--; } z[i] = 0; break; } if( z[i]>0 && z[i]<' ' ) z[i] = ' '; } blob_append(pBlob, z, -1); } #if defined(_WIN32) || defined(__BIONIC__) #ifdef __MINGW32__ #include <conio.h> |
︙ | ︙ | |||
126 127 128 129 130 131 132 133 134 135 136 137 138 139 | fossil_print("Passphrases do not match. Try again...\n"); }else{ break; } } blob_reset(&secondTry); } /* ** Prompt the user to enter a single line of text. */ void prompt_user(const char *zPrompt, Blob *pIn){ char *z; char zLine[1000]; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | fossil_print("Passphrases do not match. Try again...\n"); }else{ break; } } blob_reset(&secondTry); } /* ** Prompt to save Fossil user password */ int save_password_prompt(const char *passwd){ Blob x; char c; const char *old = db_get("last-sync-pw", 0); if( (old!=0) && fossil_strcmp(unobscure(old), passwd)==0 ){ return 0; } prompt_user("remember password (Y/n)? ", &x); c = blob_str(&x)[0]; blob_reset(&x); return ( c!='n' && c!='N' ); } /* ** Prompt for Fossil user password */ char *prompt_for_user_password(const char *zUser){ char *zPrompt = mprintf("\rpassword for %s: ", zUser); char *zPw; Blob x; fossil_force_newline(); prompt_for_password(zPrompt, &x, 0); free(zPrompt); zPw = mprintf("%b", &x); blob_reset(&x); return zPw; } /* ** Prompt the user to enter a single line of text. */ void prompt_user(const char *zPrompt, Blob *pIn){ char *z; char zLine[1000]; |
︙ | ︙ | |||
164 165 166 167 168 169 170 171 172 173 174 175 176 177 | ** ** %fossil user default ?USERNAME? ** ** Query or set the default user. The default user is the ** user for command-line interaction. ** ** %fossil user list ** ** List all users known to the repository ** ** %fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD? ** ** Create a new user in the repository. Users can never be ** deleted. They can be denied all access but they must continue | > | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | ** ** %fossil user default ?USERNAME? ** ** Query or set the default user. The default user is the ** user for command-line interaction. ** ** %fossil user list ** %fossil user ls ** ** List all users known to the repository ** ** %fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD? ** ** Create a new user in the repository. Users can never be ** deleted. They can be denied all access but they must continue |
︙ | ︙ | |||
215 216 217 218 219 220 221 | db_multi_exec( "INSERT INTO user(login,pw,cap,info,mtime)" "VALUES(%B,%Q,%B,%B,now())", &login, zPw, &caps, &contact ); free(zPw); }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){ | < > | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | db_multi_exec( "INSERT INTO user(login,pw,cap,info,mtime)" "VALUES(%B,%Q,%B,%B,now())", &login, zPw, &caps, &contact ); free(zPw); }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){ if( g.argc==3 ){ user_select(); fossil_print("%s\n", g.zLogin); }else{ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.argv[3]) ){ fossil_fatal("no such user: %s", g.argv[3]); } if( g.localOpen ){ db_lset("default-user", g.argv[3]); }else{ db_set("default-user", g.argv[3], 0); } } }else if(( n>=2 && strncmp(g.argv[2],"list",n)==0 ) || ( n>=2 && strncmp(g.argv[2],"ls",n)==0 )){ Stmt q; db_prepare(&q, "SELECT login, info FROM user ORDER BY login"); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1)); } db_finalize(&q); }else if( n>=2 && strncmp(g.argv[2],"password",2)==0 ){ |
︙ | ︙ | |||
261 262 263 264 265 266 267 | db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d", zSecret, uid); free(zSecret); } }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){ int uid; if( g.argc!=4 && g.argc!=5 ){ | | | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d", zSecret, uid); free(zSecret); } }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){ int uid; if( g.argc!=4 && g.argc!=5 ){ usage("capabilities USERNAME ?PERMISSIONS?"); } uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]); if( uid==0 ){ fossil_fatal("no such user: %s", g.argv[3]); } if( g.argc==5 ){ db_multi_exec( |
︙ | ︙ | |||
342 343 344 345 346 347 348 | if( attempt_user(fossil_getenv("USER")) ) return; if( attempt_user(fossil_getenv("LOGNAME")) ) return; if( attempt_user(fossil_getenv("USERNAME")) ) return; url_parse(0, 0); | | | 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 | if( attempt_user(fossil_getenv("USER")) ) return; if( attempt_user(fossil_getenv("LOGNAME")) ) return; if( attempt_user(fossil_getenv("USERNAME")) ) return; url_parse(0, 0); if( g.url.user && attempt_user(g.url.user) ) return; fossil_print( "Cannot figure out who you are! Consider using the --user\n" "command line option, setting your USER environment variable,\n" "or setting a default user with \"fossil user default USER\".\n" ); fossil_fatal("cannot determine user"); |
︙ | ︙ | |||
390 391 392 393 394 395 396 | int skip = atoi(PD("o","0")); Blob sql; Stmt q; int cnt = 0; int rc; login_check_credentials(); | | | 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 | int skip = atoi(PD("o","0")); Blob sql; Stmt q; int cnt = 0; int rc; login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } create_accesslog_table(); if( P("delall") && P("delallbtn") ){ db_multi_exec("DELETE FROM accesslog"); cgi_redirectf("%s/access_log?y=%d&n=%d&o=%o", g.zTop, y, n, skip); return; } |
︙ | ︙ | |||
417 418 419 420 421 422 423 | "(SELECT rowid FROM accesslog ORDER BY rowid DESC" " LIMIT -1 OFFSET 200)"); cgi_redirectf("%s/access_log?y=%d&n=%d", g.zTop, y, n); return; } style_header("Access Log"); blob_zero(&sql); | | | | | | | | | | 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 | "(SELECT rowid FROM accesslog ORDER BY rowid DESC" " LIMIT -1 OFFSET 200)"); cgi_redirectf("%s/access_log?y=%d&n=%d", g.zTop, y, n); return; } style_header("Access Log"); blob_zero(&sql); blob_append_sql(&sql, "SELECT uname, ipaddr, datetime(mtime%s), success" " FROM accesslog", timeline_utc() ); if( y==1 ){ blob_append(&sql, " WHERE success", -1); }else if( y==2 ){ blob_append(&sql, " WHERE NOT success", -1); } blob_append_sql(&sql," ORDER BY rowid DESC LIMIT %d OFFSET %d", n+1, skip); if( skip ){ style_submenu_element("Newer", "Newer entries", "%s/access_log?o=%d&n=%d&y=%d", g.zTop, skip>=n ? skip-n : 0, n, y); } rc = db_prepare_ignore_error(&q, "%s", blob_sql_text(&sql)); @ <center><table border="1" cellpadding="5" id='logtable'> @ <thead><tr><th width="33%%">Date</th><th width="34%%">User</th> @ <th width="33%%">IP Address</th></tr></thead><tbody> while( rc==SQLITE_OK && db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zIP = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); int bSuccess = db_column_int(&q, 3); cnt++; if( cnt>n ){ |
︙ | ︙ | |||
458 459 460 461 462 463 464 | } @ <td>%s(zDate)</td><td>%h(zName)</td><td>%h(zIP)</td></tr> } if( skip>0 || cnt>n ){ style_submenu_element("All", "All entries", "%s/access_log?n=10000000", g.zTop); } | | | 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | } @ <td>%s(zDate)</td><td>%h(zName)</td><td>%h(zIP)</td></tr> } if( skip>0 || cnt>n ){ style_submenu_element("All", "All entries", "%s/access_log?n=10000000", g.zTop); } @ </tbody></table></center> db_finalize(&q); @ <hr> @ <form method="post" action="%s(g.zTop)/access_log"> @ <label><input type="checkbox" name="delold"> @ Delete all but the most recent 200 entries</input></label> @ <input type="submit" name="deloldbtn" value="Delete"></input> @ </form> |
︙ | ︙ | |||
481 482 483 484 485 486 487 488 489 | @ <input type="submit" name="delfailbtn" value="Delete"></input> @ </form> @ <form method="post" action="%s(g.zTop)/access_log"> @ <label><input type="checkbox" name="delall"> @ Delete all entries</input></label> @ <input type="submit" name="delallbtn" value="Delete"></input> @ </form> style_footer(); } | > | 513 514 515 516 517 518 519 520 521 522 | @ <input type="submit" name="delfailbtn" value="Delete"></input> @ </form> @ <form method="post" action="%s(g.zTop)/access_log"> @ <label><input type="checkbox" name="delall"> @ Delete all entries</input></label> @ <input type="submit" name="delallbtn" value="Delete"></input> @ </form> output_table_sorting_javascript("logtable", "Ttt", 1); style_footer(); } |
Changes to src/utf8.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 | #include "utf8.h" #include <sqlite3.h> #ifdef _WIN32 # include <windows.h> #endif #include "cygsup.h" | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include "utf8.h" #include <sqlite3.h> #ifdef _WIN32 # include <windows.h> #endif #include "cygsup.h" #if defined(_WIN32) || defined(__CYGWIN__) /* ** Translate MBCS to UTF-8. Return a pointer to the translated text. ** Call fossil_mbcs_free() to deallocate any memory used to store the ** returned pointer when done. */ char *fossil_mbcs_to_utf8(const char *zMbcs){ extern char *sqlite3_win32_mbcs_to_utf8(const char*); |
︙ | ︙ | |||
52 53 54 55 56 57 58 | ** Return a pointer to the translated text. ** Call fossil_unicode_free() to deallocate any memory used to store the ** returned pointer when done. */ char *fossil_unicode_to_utf8(const void *zUnicode){ #if defined(_WIN32) || defined(__CYGWIN__) int nByte = WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, 0, 0, 0, 0); | | < < < > > > > > | > > | < < < > < < < < | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | ** Return a pointer to the translated text. ** Call fossil_unicode_free() to deallocate any memory used to store the ** returned pointer when done. */ char *fossil_unicode_to_utf8(const void *zUnicode){ #if defined(_WIN32) || defined(__CYGWIN__) int nByte = WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, 0, 0, 0, 0); char *zUtf = fossil_malloc( nByte ); WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, zUtf, nByte, 0, 0); return zUtf; #else static Stmt q; char *zUtf8; db_static_prepare(&q, "SELECT :utf8"); db_bind_text16(&q, ":utf8", zUnicode); db_step(&q); zUtf8 = fossil_strdup(db_column_text(&q, 0)); db_reset(&q); return zUtf8; #endif } /* ** Translate UTF-8 to unicode for use in system calls. Return a pointer to the ** translated text.. Call fossil_unicode_free() to deallocate any memory ** used to store the returned pointer when done. */ void *fossil_utf8_to_unicode(const char *zUtf8){ #if defined(_WIN32) || defined(__CYGWIN__) int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); wchar_t *zUnicode = fossil_malloc( nByte * 2 ); MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte); return zUnicode; #else assert( 0 ); /* Never used in unix */ return fossil_strdup(zUtf8); /* TODO: implement for unix */ #endif } /* ** Deallocate any memory that was previously allocated by ** fossil_unicode_to_utf8(). */ void fossil_unicode_free(void *pOld){ fossil_free(pOld); } #if defined(__APPLE__) && !defined(WITHOUT_ICONV) # include <iconv.h> #endif /* |
︙ | ︙ | |||
127 128 129 130 131 132 133 | } WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, zUtf, nByte, 0, 0); pUtf = qUtf = zUtf; while( *pUtf ) { if( *pUtf == (char)0xef ){ wchar_t c = ((pUtf[1]&0x3f)<<6)|(pUtf[2]&0x3f); /* Only really convert it when the resulting char is in range. */ | | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | } WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, zUtf, nByte, 0, 0); pUtf = qUtf = zUtf; while( *pUtf ) { if( *pUtf == (char)0xef ){ wchar_t c = ((pUtf[1]&0x3f)<<6)|(pUtf[2]&0x3f); /* Only really convert it when the resulting char is in range. */ if( c && ((c < ' ') || wcschr(L"\"*:<>?|", c)) ){ *qUtf++ = c; pUtf+=3; continue; } } *qUtf++ = *pUtf++; } *qUtf = 0; return zUtf; |
︙ | ︙ | |||
177 178 179 180 181 182 183 | ** Translate text from UTF-8 to the filename character set. ** Return a pointer to the translated text. ** Call fossil_filename_free() to deallocate any memory used to store the ** returned pointer when done. ** ** On Windows, characters in the range U+0001 to U+0031 and the ** characters '"', '*', ':', '<', '>', '?' and '|' are invalid | > | > | > | > > > > > > > > > > > > > > > | > > > > > > > > > | > > > > > > > > > > > > | | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | ** Translate text from UTF-8 to the filename character set. ** Return a pointer to the translated text. ** Call fossil_filename_free() to deallocate any memory used to store the ** returned pointer when done. ** ** On Windows, characters in the range U+0001 to U+0031 and the ** characters '"', '*', ':', '<', '>', '?' and '|' are invalid ** to be used, except in the 'extended path' prefix ('?') and ** as drive specifier (':'). Therefore, translate those to characters ** in the range U+F001 - U+F07F (private use area), so those ** characters never arrive in any Windows API. The filenames might ** look strange in Windows explorer, but in the cygwin shell ** everything looks as expected. ** ** See: <http://cygwin.com/cygwin-ug-net/using-specialnames.html> ** */ void *fossil_utf8_to_filename(const char *zUtf8){ #ifdef _WIN32 int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); /* Overallocate 6 chars, making some room for extended paths */ wchar_t *zUnicode = sqlite3_malloc( (nChar+6) * sizeof(wchar_t) ); wchar_t *wUnicode = zUnicode; if( zUnicode==0 ){ return 0; } MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar); /* ** If path starts with "//?/" or "\\?\" (extended path), translate ** any slashes to backslashes but leave the '?' intact */ if( (zUtf8[0]=='\\' || zUtf8[0]=='/') && (zUtf8[1]=='\\' || zUtf8[1]=='/') && zUtf8[2]=='?' && (zUtf8[3]=='\\' || zUtf8[3]=='/')) { wUnicode[0] = wUnicode[1] = wUnicode[3] = '\\'; zUtf8 += 4; wUnicode += 4; } /* ** If there is no "\\?\" prefix but there is a drive or UNC ** path prefix and the path is larger than MAX_PATH chars, ** no Win32 API function can handle that unless it is ** prefixed with the extended path prefix. See: ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath> **/ if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':' && (zUtf8[2]=='\\' || zUtf8[2]=='/') ){ if( wUnicode==zUnicode && nChar>MAX_PATH){ memmove(wUnicode+4, wUnicode, nChar*sizeof(wchar_t)); memcpy(wUnicode, L"\\\\?\\", 4*sizeof(wchar_t)); wUnicode += 4; } /* ** If (remainder of) path starts with "<drive>:/" or "<drive>:\", ** leave the ':' intact but translate the backslash to a slash. */ wUnicode[2] = '\\'; wUnicode += 3; }else if( wUnicode==zUnicode && nChar>MAX_PATH && (zUtf8[0]=='\\' || zUtf8[0]=='/') && (zUtf8[1]=='\\' || zUtf8[1]=='/') && zUtf8[2]!='?'){ memmove(wUnicode+6, wUnicode, nChar*sizeof(wchar_t)); memcpy(wUnicode, L"\\\\?\\UNC", 7*sizeof(wchar_t)); wUnicode += 7; } /* ** In the remainder of the path, translate invalid characters to ** characters in the Unicode private use area. This is what makes ** Win32 fossil.exe work well in a Cygwin environment even when a ** filename contains characters which are invalid for Win32. */ while( *wUnicode != '\0' ){ if( (*wUnicode < ' ') || wcschr(L"\"*:<>?|", *wUnicode) ){ *wUnicode |= 0xF000; }else if( *wUnicode == '/' ){ *wUnicode = '\\'; } ++wUnicode; } return zUnicode; |
︙ | ︙ | |||
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | ** to the console. */ int fossil_utf8_to_console(const char *zUtf8, int nByte, int toStdErr){ #ifdef _WIN32 int nChar, written = 0; wchar_t *zUnicode; /* Unicode version of zUtf8 */ DWORD dummy; static int istty[2] = { -1, -1 }; if( istty[toStdErr] == -1 ){ istty[toStdErr] = _isatty(toStdErr + 1) != 0; } if( !istty[toStdErr] ){ /* stdout/stderr is not a console. */ return -1; } | > > > > > > | > | > > | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | ** to the console. */ int fossil_utf8_to_console(const char *zUtf8, int nByte, int toStdErr){ #ifdef _WIN32 int nChar, written = 0; wchar_t *zUnicode; /* Unicode version of zUtf8 */ DWORD dummy; Blob blob; static int istty[2] = { -1, -1 }; if( istty[toStdErr] == -1 ){ istty[toStdErr] = _isatty(toStdErr + 1) != 0; } if( !istty[toStdErr] ){ /* stdout/stderr is not a console. */ return -1; } /* If blob to be written to the Windows console is not * UTF-8, convert it to UTF-8 first. */ blob_init(&blob, zUtf8, nByte); blob_to_utf8_no_bom(&blob, 1); nChar = MultiByteToWideChar(CP_UTF8, 0, blob_buffer(&blob), blob_size(&blob), NULL, 0); zUnicode = malloc( (nChar + 1) *sizeof(zUnicode[0]) ); if( zUnicode==0 ){ return 0; } nChar = MultiByteToWideChar(CP_UTF8, 0, blob_buffer(&blob), blob_size(&blob), zUnicode, nChar); blob_reset(&blob); /* Split WriteConsoleW call into multiple chunks, if necessary. See: * <https://connect.microsoft.com/VisualStudio/feedback/details/635230> */ while( written < nChar ){ int size = nChar-written; if( size > 26000 ) size = 26000; WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE - toStdErr), zUnicode+written, size, &dummy, 0); |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
76 77 78 79 80 81 82 83 84 85 86 87 88 89 | rc = _wsystem(zUnicode); fossil_unicode_free(zUnicode); free(zNewCmd); #else /* On unix, evaluate the command directly. */ if( g.fSystemTrace ) fprintf(stderr, "SYSTEM: %s\n", zOrigCmd); rc = system(zOrigCmd); #endif return rc; } /* ** Like strcmp() except that it accepts NULL pointers. NULL sorts before | > > > > > > > > > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | rc = _wsystem(zUnicode); fossil_unicode_free(zUnicode); free(zNewCmd); #else /* On unix, evaluate the command directly. */ if( g.fSystemTrace ) fprintf(stderr, "SYSTEM: %s\n", zOrigCmd); /* Unix systems should never shell-out while processing an HTTP request, ** either via CGI, SCGI, or direct HTTP. The following assert verifies ** this. And the following assert proves that Fossil is not vulnerable ** to the ShellShock or BashDoor bug. */ assert( g.cgiOutput==0 ); /* The regular system() call works to get a shell on unix */ rc = system(zOrigCmd); #endif return rc; } /* ** Like strcmp() except that it accepts NULL pointers. NULL sorts before |
︙ | ︙ | |||
293 294 295 296 297 298 299 | ** Returns true (non-0) if the given timer ID (as returned from ** fossil_timer_start() is currently active. */ int fossil_timer_is_active( int timerId ){ if(timerId<1 || timerId>FOSSIL_TIMER_COUNT){ return 0; }else{ | | > > > > > > > > > > > > > > > > > > > > | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | ** Returns true (non-0) if the given timer ID (as returned from ** fossil_timer_start() is currently active. */ int fossil_timer_is_active( int timerId ){ if(timerId<1 || timerId>FOSSIL_TIMER_COUNT){ return 0; }else{ const int rc = fossilTimerList[timerId-1].id; assert(!rc || (rc == timerId)); return fossilTimerList[timerId-1].id; } } /* ** Return TRUE if fd is a valid open file descriptor. This only ** works on unix. The function always returns true on Windows. */ int is_valid_fd(int fd){ #ifdef _WIN32 return 1; #else return fcntl(fd, F_GETFL)!=(-1) || errno!=EBADF; #endif } /* ** Returns TRUE if zSym is exactly UUID_SIZE bytes long and contains ** only lower-case ASCII hexadecimal values. */ int fossil_is_uuid(const char *zSym){ return zSym && (UUID_SIZE==strlen(zSym)) && validate16(zSym, UUID_SIZE); } /* ** Return true if the input string is NULL or all whitespace. ** Return false if the input string contains text. */ int fossil_all_whitespace(const char *z){ if( z==0 ) return 1; while( fossil_isspace(z[0]) ){ z++; } return z[0]==0; } |
Changes to src/verify.c.
︙ | ︙ | |||
61 62 63 64 65 66 67 | ** The following bag holds the rid for every record that needs ** to be verified. */ static Bag toVerify; static int inFinalVerify = 0; /* | | | | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | ** The following bag holds the rid for every record that needs ** to be verified. */ static Bag toVerify; static int inFinalVerify = 0; /* ** This routine is called just prior to each commit operation. ** ** Invoke verify_rid() on every record that has been added or modified ** in the repository, in order to make sure that the repository is sane. */ static int verify_at_commit(void){ int rid; content_clear_cache(); inFinalVerify = 1; rid = bag_first(&toVerify); while( rid>0 ){ verify_rid(rid); rid = bag_next(&toVerify, rid); } bag_clear(&toVerify); inFinalVerify = 0; return 0; } /* ** Arrange to verify a particular record prior to committing. ** ** If the record rid is less than 1, then just initialize the ** verification system but do not record anything as needing ** verification. */ void verify_before_commit(int rid){ static int isInit = 0; if( !isInit ){ |
︙ | ︙ |
Changes to src/vfile.c.
︙ | ︙ | |||
43 44 45 46 47 48 49 | /* ** Given a UUID, return the corresponding record ID. If the UUID ** does not exist, then return 0. ** ** For this routine, the UUID must be exact. For a match against ** user input with mixed case, use resolve_uuid(). ** | | | | > | | | | > > > | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | /* ** Given a UUID, return the corresponding record ID. If the UUID ** does not exist, then return 0. ** ** For this routine, the UUID must be exact. For a match against ** user input with mixed case, use resolve_uuid(). ** ** If the UUID is not found and phantomize is 1 or 2, then attempt to ** create a phantom record. A private phantom is created for 2 and ** a public phantom is created for 1. */ int uuid_to_rid(const char *zUuid, int phantomize){ int rid, sz; char z[UUID_SIZE+1]; sz = strlen(zUuid); if( sz!=UUID_SIZE || !validate16(zUuid, sz) ){ return 0; } memcpy(z, zUuid, UUID_SIZE+1); canonical16(z, sz); rid = fast_uuid_to_rid(z); if( rid==0 && phantomize ){ rid = content_new(zUuid, phantomize-1); } return rid; } /* ** Load a vfile from a record ID. Return the number of files with ** missing content. */ int load_vfile_from_rid(int vid){ int rid, size, nMissing; Stmt ins, ridq; Manifest *p; ManifestFile *pFile; if( db_exists("SELECT 1 FROM vfile WHERE vid=%d", vid) ){ return 0; } db_begin_transaction(); p = manifest_get(vid, CFTYPE_MANIFEST, 0); if( p==0 ) { db_end_transaction(1); return 0; } db_prepare(&ins, "INSERT INTO vfile(vid,isexe,islink,rid,mrid,pathname) " " VALUES(:vid,:isexe,:islink,:id,:id,:name)"); db_prepare(&ridq, "SELECT rid,size FROM blob WHERE uuid=:uuid"); db_bind_int(&ins, ":vid", vid); manifest_file_rewind(p); nMissing = 0; while( (pFile = manifest_file_next(p,0))!=0 ){ if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue; db_bind_text(&ridq, ":uuid", pFile->zUuid); if( db_step(&ridq)==SQLITE_ROW ){ rid = db_column_int(&ridq, 0); size = db_column_int(&ridq, 1); }else{ rid = 0; size = 0; } db_reset(&ridq); if( rid==0 || size<0 ){ fossil_warning("content missing for %s", pFile->zName); nMissing++; continue; } db_bind_int(&ins, ":isexe", ( manifest_file_mperm(pFile)==PERM_EXE )); db_bind_int(&ins, ":id", rid); db_bind_text(&ins, ":name", pFile->zName); db_bind_int(&ins, ":islink", ( manifest_file_mperm(pFile)==PERM_LNK )); db_step(&ins); db_reset(&ins); } db_finalize(&ridq); db_finalize(&ins); manifest_destroy(p); db_end_transaction(0); return nMissing; } #if INTERFACE /* ** The cksigFlags parameter to vfile_check_signature() is an OR-ed ** combination of the following bits: */ |
︙ | ︙ | |||
138 139 140 141 142 143 144 | ** VFILE.CHNGED field according to whether or not ** the file has changed. 0 means no change. 1 means edited. 2 means ** the file has changed due to a merge. 3 means the file was added ** by a merge. ** ** If VFILE.DELETED is true or if VFILE.RID is zero, then the file was either ** removed from configuration management via "fossil rm" or added via | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | ** VFILE.CHNGED field according to whether or not ** the file has changed. 0 means no change. 1 means edited. 2 means ** the file has changed due to a merge. 3 means the file was added ** by a merge. ** ** If VFILE.DELETED is true or if VFILE.RID is zero, then the file was either ** removed from configuration management via "fossil rm" or added via ** "fossil add", respectively, and in both cases we always know that ** the file has changed without having the check the size, mtime, ** or on-disk content. ** ** If the size of the file has changed, then we always know that the file ** changed without having to look at the mtime or on-disk content. ** ** The mtime of the file is only a factor if the mtime-changes setting |
︙ | ︙ | |||
183 184 185 186 187 188 189 | id = db_column_int(&q, 0); zName = db_column_text(&q, 1); rid = db_column_int(&q, 2); isDeleted = db_column_int(&q, 3); oldChnged = chnged = db_column_int(&q, 4); oldMtime = db_column_int64(&q, 7); | < > | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | id = db_column_int(&q, 0); zName = db_column_text(&q, 1); rid = db_column_int(&q, 2); isDeleted = db_column_int(&q, 3); oldChnged = chnged = db_column_int(&q, 4); oldMtime = db_column_int64(&q, 7); origSize = db_column_int64(&q, 6); currentSize = file_wd_size(zName); currentMtime = file_wd_mtime(0); if( chnged==0 && (isDeleted || rid==0) ){ /* "fossil rm" or "fossil add" always change the file */ chnged = 1; }else if( !file_wd_isfile_or_link(0) && currentSize>=0 ){ if( cksigFlags & CKSIG_ENOTFILE ){ fossil_warning("not an ordinary file: %s", zName); |
︙ | ︙ | |||
313 314 315 316 317 318 319 | continue; } } if( verbose ) fossil_print("%s\n", &zName[nRepos]); if( file_wd_isdir(zName) == 1 ){ /*TODO(dchest): remove directories? */ fossil_fatal("%s is directory, cannot overwrite\n", zName); | | | | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | continue; } } if( verbose ) fossil_print("%s\n", &zName[nRepos]); if( file_wd_isdir(zName) == 1 ){ /*TODO(dchest): remove directories? */ fossil_fatal("%s is directory, cannot overwrite\n", zName); } if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(0)) ){ file_delete(zName); } if( isLink ){ symlink_create(blob_str(&content), zName); }else{ blob_write_to_file(&content, zName); } |
︙ | ︙ | |||
390 391 392 393 394 395 396 | static const char *const azTemp[] = { "baseline", "merge", "original", "output", }; int i, j, n; | | | | | | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | static const char *const azTemp[] = { "baseline", "merge", "original", "output", }; int i, j, n; if( sqlite3_strglob("ci-comment-????????????.txt", zName)==0 ) return 1; for(; zName[0]!=0; zName++){ if( zName[0]=='/' && sqlite3_strglob("/ci-comment-????????????.txt", zName)==0 ){ return 1; } if( zName[0]!='-' ) continue; for(i=0; i<sizeof(azTemp)/sizeof(azTemp[0]); i++){ n = (int)strlen(azTemp[i]); if( memcmp(azTemp[i], zName+1, n) ) continue; if( zName[n+1]==0 ) return 1; if( zName[n+1]=='-' ){ for(j=n+2; zName[j] && fossil_isdigit(zName[j]); j++){} if( zName[j]==0 ) return 1; } } } return 0; } #if INTERFACE /* ** Values for the scanFlags parameter to vfile_scan(). |
︙ | ︙ | |||
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | } zUtf8 = fossil_filename_to_utf8(pEntry->d_name); blob_appendf(pPath, "/%s", zUtf8); zPath = blob_str(pPath); if( glob_match(pIgnore1, &zPath[nPrefix+1]) || glob_match(pIgnore2, &zPath[nPrefix+1]) ){ /* do nothing */ }else if( file_wd_isdir(zPath)==1 ){ if( !vfile_top_of_checkout(zPath) ){ vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2); } }else if( file_wd_isfile_or_link(zPath) ){ if( (scanFlags & SCAN_TEMP)==0 || is_temporary_file(zUtf8) ){ db_bind_text(&ins, ":file", &zPath[nPrefix+1]); db_step(&ins); db_reset(&ins); } } fossil_filename_free(zUtf8); | > > > > > > > > > > | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 | } zUtf8 = fossil_filename_to_utf8(pEntry->d_name); blob_appendf(pPath, "/%s", zUtf8); zPath = blob_str(pPath); if( glob_match(pIgnore1, &zPath[nPrefix+1]) || glob_match(pIgnore2, &zPath[nPrefix+1]) ){ /* do nothing */ #ifdef _DIRENT_HAVE_D_TYPE }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) ? (file_wd_isdir(zPath)==1) : (pEntry->d_type==DT_DIR) ){ #else }else if( file_wd_isdir(zPath)==1 ){ #endif if( !vfile_top_of_checkout(zPath) ){ vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2); } #ifdef _DIRENT_HAVE_D_TYPE }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) ? (file_wd_isfile_or_link(zPath)) : (pEntry->d_type==DT_REG) ){ #else }else if( file_wd_isfile_or_link(zPath) ){ #endif if( (scanFlags & SCAN_TEMP)==0 || is_temporary_file(zUtf8) ){ db_bind_text(&ins, ":file", &zPath[nPrefix+1]); db_step(&ins); db_reset(&ins); } } fossil_filename_free(zUtf8); |
︙ | ︙ | |||
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 | zUtf8 = fossil_filename_to_utf8(pEntry->d_name); blob_appendf(pPath, "/%s", zUtf8); zPath = blob_str(pPath); if( glob_match(pIgnore1, &zPath[nPrefix+1]) || glob_match(pIgnore2, &zPath[nPrefix+1]) || glob_match(pIgnore3, &zPath[nPrefix+1]) ){ /* do nothing */ }else if( file_wd_isdir(zPath)==1 ){ if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){ char *zSavePath = mprintf("%s", zPath); int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2, pIgnore3); db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]); db_bind_int(&ins, ":count", count); db_step(&ins); db_reset(&ins); fossil_free(zSavePath); result += count; /* found X normal files? */ } }else if( file_wd_isfile_or_link(zPath) ){ db_bind_text(&upd, ":file", zOrigPath); db_step(&upd); db_reset(&upd); result++; /* found 1 normal file */ } fossil_filename_free(zUtf8); blob_resize(pPath, origSize); | > > > > > > > > > > | 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 | zUtf8 = fossil_filename_to_utf8(pEntry->d_name); blob_appendf(pPath, "/%s", zUtf8); zPath = blob_str(pPath); if( glob_match(pIgnore1, &zPath[nPrefix+1]) || glob_match(pIgnore2, &zPath[nPrefix+1]) || glob_match(pIgnore3, &zPath[nPrefix+1]) ){ /* do nothing */ #ifdef _DIRENT_HAVE_D_TYPE }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) ? (file_wd_isdir(zPath)==1) : (pEntry->d_type==DT_DIR) ){ #else }else if( file_wd_isdir(zPath)==1 ){ #endif if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){ char *zSavePath = mprintf("%s", zPath); int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2, pIgnore3); db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]); db_bind_int(&ins, ":count", count); db_step(&ins); db_reset(&ins); fossil_free(zSavePath); result += count; /* found X normal files? */ } #ifdef _DIRENT_HAVE_D_TYPE }else if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK) ? (file_wd_isfile_or_link(zPath)) : (pEntry->d_type==DT_REG) ){ #else }else if( file_wd_isfile_or_link(zPath) ){ #endif db_bind_text(&upd, ":file", zOrigPath); db_step(&upd); db_reset(&upd); result++; /* found 1 normal file */ } fossil_filename_free(zUtf8); blob_resize(pPath, origSize); |
︙ | ︙ | |||
652 653 654 655 656 657 658 | */ void vfile_aggregate_checksum_disk(int vid, Blob *pOut){ FILE *in; Stmt q; char zBuf[4096]; db_must_be_within_tree(); | | | | 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 | */ void vfile_aggregate_checksum_disk(int vid, Blob *pOut){ FILE *in; Stmt q; char zBuf[4096]; db_must_be_within_tree(); db_prepare(&q, "SELECT %Q || pathname, pathname, origname, is_selected(id), rid" " FROM vfile" " WHERE (NOT deleted OR NOT is_selected(id)) AND vid=%d" " ORDER BY if_selected(id, pathname, origname) /*scan*/", g.zLocalRoot, vid ); md5sum_init(); while( db_step(&q)==SQLITE_ROW ){ const char *zFullpath = db_column_text(&q, 0); const char *zName = db_column_text(&q, 1); int isSelected = db_column_int(&q, 3); if( isSelected ){ md5sum_step_text(zName, -1); if( file_wd_islink(zFullpath) ){ /* Instead of file content, use link destination path */ Blob pathBuf; sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", blob_read_link(&pathBuf, zFullpath)); md5sum_step_text(zBuf, -1); md5sum_step_text(blob_str(&pathBuf), -1); blob_reset(&pathBuf); }else{ in = fossil_fopen(zFullpath,"rb"); if( in==0 ){ |
︙ | ︙ | |||
741 742 743 744 745 746 747 | ** the working check-out on disk. Report any errors. */ void vfile_compare_repository_to_disk(int vid){ int rc; Stmt q; Blob disk, repo; char *zOut; | | | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 | ** the working check-out on disk. Report any errors. */ void vfile_compare_repository_to_disk(int vid){ int rc; Stmt q; Blob disk, repo; char *zOut; db_must_be_within_tree(); db_prepare(&q, "SELECT %Q || pathname, pathname, rid FROM vfile" " WHERE NOT deleted AND vid=%d AND is_selected(id)" " ORDER BY if_selected(id, pathname, origname) /*scan*/", g.zLocalRoot, vid ); md5sum_init(); while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
807 808 809 810 811 812 813 | */ void vfile_aggregate_checksum_repository(int vid, Blob *pOut){ Blob file; Stmt q; char zBuf[100]; db_must_be_within_tree(); | | | 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 | */ void vfile_aggregate_checksum_repository(int vid, Blob *pOut){ Blob file; Stmt q; char zBuf[100]; db_must_be_within_tree(); db_prepare(&q, "SELECT pathname, origname, rid, is_selected(id)" " FROM vfile" " WHERE (NOT deleted OR NOT is_selected(id))" " AND rid>0 AND vid=%d" " ORDER BY if_selected(id,pathname,origname) /*scan*/", vid); blob_zero(&file); |
︙ | ︙ | |||
845 846 847 848 849 850 851 | ** ** Return the resulting checksum in blob pOut. ** ** If pManOut is not NULL then fill it with the checksum found in the ** "R" card near the end of the manifest. ** ** In a well-formed manifest, the two checksums computed here, pOut and | | | 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 | ** ** Return the resulting checksum in blob pOut. ** ** If pManOut is not NULL then fill it with the checksum found in the ** "R" card near the end of the manifest. ** ** In a well-formed manifest, the two checksums computed here, pOut and ** pManOut, should be identical. */ void vfile_aggregate_checksum_manifest(int vid, Blob *pOut, Blob *pManOut){ int fid; Blob file; Blob err; Manifest *pManifest; ManifestFile *pFile; |
︙ | ︙ |
Changes to src/wiki.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to do formatting of wiki text. */ #include <assert.h> #include <ctype.h> | > < | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to do formatting of wiki text. */ #include "config.h" #include <assert.h> #include <ctype.h> #include "wiki.h" /* ** Return true if the input string is a well-formed wiki page name. ** ** Well-formed wiki page names do not begin or end with whitespace, ** and do not contain tabs or other control characters and do not ** contain more than a single space character in a row. Well-formed ** names must be between 1 and 100 characters in length, inclusive. */ int wiki_name_is_wellformed(const unsigned char *z){ int i; if( z[0]<=0x20 ){ return 0; } for(i=1; z[i]; i++){ if( z[i]<0x20 ) return 0; if( z[i]==0x20 && z[i-1]==0x20 ) return 0; } if( z[i-1]==' ' ) return 0; if( i<1 || i>100 ) return 0; return 1; } /* ** Output rules for well-formed wiki pages */ static void well_formed_wiki_name_rules(void){ @ <ul> @ <li> Must not begin or end with a space.</li> @ <li> Must not contain any control characters, including tab or @ newline.</li> @ <li> Must not have two or more spaces in a row internally.</li> @ <li> Must be between 1 and 100 characters in length.</li> @ </ul> } /* ** Check a wiki name. If it is not well-formed, then issue an error ** and return true. If it is well-formed, return false. */ |
︙ | ︙ | |||
132 133 134 135 136 137 138 | ){ return zMimetype; } return "text/x-fossil-wiki"; } /* | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < | < < < < < < < < | < < < | < < < | < < < < < < < < > > > | | | | | | | > | | | | | | | > | | | | | | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | ){ return zMimetype; } return "text/x-fossil-wiki"; } /* ** Render wiki text according to its mimetype. ** ** text/x-fossil-wiki Fossil wiki ** text/x-markdown Markdown ** anything else... Plain text */ void wiki_render_by_mimetype(Blob *pWiki, const char *zMimetype){ if( zMimetype==0 || fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ wiki_convert(pWiki, 0, 0); }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ Blob title = BLOB_INITIALIZER; Blob tail = BLOB_INITIALIZER; markdown_to_html(pWiki, &title, &tail); #if 0 if( blob_size(&title)>0 ){ @ <h1>%s(blob_str(&title))</h1> } #endif @ %s(blob_str(&tail)) blob_reset(&title); blob_reset(&tail); }else{ @ <pre> @ %h(blob_str(pWiki)) @ </pre> } } /* ** WEBPAGE: md_rules ** ** Show a summary of the Markdown wiki formatting rules. */ void markdown_rules_page(void){ Blob x; int fTxt = P("txt")!=0; style_header("Markdown Formatting Rules"); if( fTxt ){ style_submenu_element("Formatted", "Formatted", "%R/md_rules"); }else{ style_submenu_element("Plain-Text", "Plain-Text", "%R/md_rules?txt=1"); } blob_init(&x, builtin_text("markdown.md"), -1); wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown"); blob_reset(&x); style_footer(); } /* ** Returns non-zero if moderation is required for wiki changes and wiki ** attachments. */ int wiki_need_moderation( int localUser /* Are we being called for a local interactive user? */ ){ /* ** If the FOSSIL_FORCE_WIKI_MODERATION variable is set, *ALL* changes for ** wiki pages will be required to go through moderation (even those performed ** by the local interactive user via the command line). This can be useful ** for local (or remote) testing of the moderation subsystem and its impact ** on the contents and status of wiki pages. */ if( fossil_getenv("FOSSIL_FORCE_WIKI_MODERATION")!=0 ){ return 1; } if( localUser ){ return 0; } return g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1; } /* Standard submenu items for wiki pages */ #define W_SRCH 0x00001 #define W_LIST 0x00002 #define W_HELP 0x00004 #define W_NEW 0x00008 #define W_BLOG 0x00010 #define W_SANDBOX 0x00020 #define W_ALL 0x0001f #define W_ALL_BUT(x) (W_ALL&~(x)) /* ** Add some standard submenu elements for wiki screens. */ static void wiki_standard_submenu(unsigned int ok){ if( (ok & W_SRCH)!=0 && search_restrict(SRCH_WIKI)!=0 ){ style_submenu_element("Search","Search","%R/wikisrch"); } if( (ok & W_LIST)!=0 ){ style_submenu_element("List","List","%R/wcontent"); } if( (ok & W_HELP)!=0 ){ style_submenu_element("Help","Help","%R/wikihelp"); } if( (ok & W_NEW)!=0 && g.anon.NewWiki ){ style_submenu_element("New","New","%R/wikinew"); } #if 0 if( (ok & W_BLOG)!=0 #endif if( (ok & W_SANDBOX)!=0 ){ style_submenu_element("Sandbox", "Sandbox", "%R/wiki?name=Sandbox"); } } /* ** WEBPAGE: wikihelp ** A generic landing page for wiki. */ void wiki_helppage(void){ login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } style_header("Wiki Help"); wiki_standard_submenu(W_ALL_BUT(W_HELP)); @ <h2>Wiki Links</h2> @ <ul> { char *zWikiHomePageName = db_get("index-page",0); if( zWikiHomePageName ){ @ <li> %z(href("%R%s",zWikiHomePageName)) @ %h(zWikiHomePageName)</a> wiki home page.</li> } } { char *zHomePageName = db_get("project-name",0); if( zHomePageName ){ @ <li> %z(href("%R/wiki?name=%t",zHomePageName)) @ %h(zHomePageName)</a> project home page.</li> } } @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li> @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li> @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a> @ to experiment.</li> if( g.anon.NewWiki ){ @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li> if( g.anon.Write ){ @ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li> } } @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a> @ available on this server.</li> if( g.anon.ModWiki ){ @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li> } if( search_restrict(SRCH_WIKI)!=0 ){ @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key @ words</li> } @ </ul> style_footer(); return; } /* ** WEBPAGE: wikisrch ** Usage: /wikisrch?s=PATTERN ** ** Full-text search of all current wiki text */ void wiki_srchpage(void){ login_check_credentials(); style_header("Wiki Search"); wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX); search_screen(SRCH_WIKI, 0); style_footer(); } /* ** WEBPAGE: wiki ** URL: /wiki?name=PAGENAME */ void wiki_page(void){ char *zTag; int rid = 0; int isSandbox; char *zUuid; unsigned submenuFlags = W_ALL; Blob wiki; Manifest *pWiki = 0; const char *zPageName; const char *zMimetype = 0; char *zBody = mprintf("%s","<i>Empty Page</i>"); login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } zPageName = P("name"); if( zPageName==0 ){ if( search_restrict(SRCH_WIKI)!=0 ){ wiki_srchpage(); }else{ wiki_helppage(); } return; } if( check_name(zPageName) ) return; isSandbox = is_sandbox(zPageName); if( isSandbox ){ submenuFlags &= ~W_SANDBOX; zBody = db_get("sandbox",zBody); zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki"); rid = 0; }else{ const char *zUuid = P("id"); if( zUuid==0 || (rid = symbolic_name_to_rid(zUuid,"w"))==0 ){ zTag = mprintf("wiki-%s", zPageName); rid = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" " ORDER BY mtime DESC", zTag ); free(zTag); } pWiki = manifest_get(rid, CFTYPE_WIKI, 0); if( pWiki ){ zBody = pWiki->zWiki; zMimetype = pWiki->zMimetype; } } zMimetype = wiki_filter_mimetypes(zMimetype); if( !g.isHome ){ if( rid ){ style_submenu_element("Diff", "Last change", "%R/wdiff?name=%T&a=%d", zPageName, rid); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); style_submenu_element("Details", "Details", "%R/info/%s", zUuid); } if( (rid && g.anon.WrWiki) || (!rid && g.anon.NewWiki) ){ if( db_get_boolean("wysiwyg-wiki", 0) ){ style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T&wysiwyg=1", g.zTop, zPageName); }else{ style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T", g.zTop, zPageName); } } if( rid && g.anon.ApndWiki && g.anon.Attach ){ style_submenu_element("Attach", "Add An Attachment", "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T", g.zTop, zPageName, g.zTop, zPageName); } if( rid && g.anon.ApndWiki ){ style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T&mimetype=%s", g.zTop, zPageName, zMimetype); } if( g.perm.Hyperlink ){ style_submenu_element("History", "History", "%s/whistory?name=%T", g.zTop, zPageName); } } style_set_current_page("%T?name=%T", g.zPath, zPageName); style_header("%s", zPageName); wiki_standard_submenu(submenuFlags); blob_init(&wiki, zBody, -1); wiki_render_by_mimetype(&wiki, zMimetype); blob_reset(&wiki); attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>"); manifest_destroy(pWiki); style_footer(); } /* ** Write a wiki artifact into the repository */ static void wiki_put(Blob *pWiki, int parent, int needMod){ int nrid; if( !needMod ){ nrid = content_put_ex(pWiki, 0, 0, 0, 0); if( parent) content_deltify(parent, nrid, 0); }else{ nrid = content_put_ex(pWiki, 0, 0, 0, 1); moderation_table_create(); db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid); } db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid); manifest_crosslink(nrid, pWiki, MC_NONE); } /* ** Formal names and common names for the various wiki styles. */ static const char *const azStyles[] = { "text/x-fossil-wiki", "Fossil Wiki", "text/x-markdown", "Markdown", "text/plain", "Plain Text" }; /* ** Output a selection box from which the user can select the ** wiki mimetype. */ void mimetype_option_menu(const char *zMimetype){ unsigned i; @ <select name="mimetype" size="1"> for(i=0; i<sizeof(azStyles)/sizeof(azStyles[0]); i+=2){ if( fossil_strcmp(zMimetype,azStyles[i])==0 ){ @ <option value="%s(azStyles[i])" selected>%s(azStyles[i+1])</option> }else{ @ <option value="%s(azStyles[i])">%s(azStyles[i+1])</option> } } |
︙ | ︙ | |||
370 371 372 373 374 375 376 | } login_check_credentials(); zPageName = PD("name",""); if( check_name(zPageName) ) return; isSandbox = is_sandbox(zPageName); if( isSandbox ){ if( !g.perm.WrWiki ){ | | | | | 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 | } login_check_credentials(); zPageName = PD("name",""); if( check_name(zPageName) ) return; isSandbox = is_sandbox(zPageName); if( isSandbox ){ if( !g.perm.WrWiki ){ login_needed(g.anon.WrWiki); return; } if( zBody==0 ){ zBody = db_get("sandbox",""); zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki"); } }else{ zTag = mprintf("wiki-%s", zPageName); rid = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" " ORDER BY mtime DESC", zTag ); free(zTag); if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){ login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki); return; } if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){ zBody = pWiki->zWiki; zMimetype = pWiki->zMimetype; } } |
︙ | ︙ | |||
418 419 420 421 422 423 424 | blob_appendf(&wiki, "N %s\n", zMimetype); } if( rid ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); blob_appendf(&wiki, "P %s\n", zUuid); free(zUuid); } | | | | | | | | 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 | blob_appendf(&wiki, "N %s\n", zMimetype); } if( rid ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); blob_appendf(&wiki, "P %s\n", zUuid); free(zUuid); } if( !login_is_nobody() ){ blob_appendf(&wiki, "U %F\n", login_name()); } blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody); md5sum_blob(&wiki, &cksum); blob_appendf(&wiki, "Z %b\n", &cksum); blob_reset(&cksum); wiki_put(&wiki, 0, wiki_need_moderation(0)); } db_end_transaction(0); cgi_redirectf("wiki?name=%T", zPageName); } if( P("cancel")!=0 ){ cgi_redirectf("wiki?name=%T", zPageName); return; } if( zBody==0 ){ zBody = mprintf("<i>Empty Page</i>"); } style_set_current_page("%T?name=%T", g.zPath, zPageName); style_header("Edit: %s", zPageName); if( !goodCaptcha ){ @ <p class="generalError">Error: Incorrect security code.</p> } blob_zero(&wiki); blob_append(&wiki, zBody, -1); if( P("preview")!=0 ){ @ Preview:<hr /> wiki_render_by_mimetype(&wiki, zMimetype); @ <hr /> blob_reset(&wiki); } for(n=2, z=zBody; z[0]; z++){ if( z[0]=='\n' ) n++; } if( n<20 ) n = 20; if( n>30 ) n = 30; if( !isWysiwyg ){ /* Traditional markup-only editing */ form_begin(0, "%R/wikiedit"); @ <div>Markup style: mimetype_option_menu(zMimetype); @ <br /><textarea name="w" class="wikiedit" cols="80" @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> @ <br /> if( db_get_boolean("wysiwyg-wiki", 0) ){ @ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor" @ onclick='return confirm("Switching to WYSIWYG-mode\nwill erase your markup\nedits. Continue?")' /> } @ <input type="submit" name="preview" value="Preview Your Changes" /> |
︙ | ︙ | |||
510 511 512 513 514 515 516 | ** to the wikiedit screen for that new page. */ void wikinew_page(void){ const char *zName; const char *zMimetype; login_check_credentials(); if( !g.perm.NewWiki ){ | | | > > | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 | ** to the wikiedit screen for that new page. */ void wikinew_page(void){ const char *zName; const char *zMimetype; login_check_credentials(); if( !g.perm.NewWiki ){ login_needed(g.anon.NewWiki); return; } zName = PD("name",""); zMimetype = wiki_filter_mimetypes(P("mimetype")); if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){ if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 && db_get_boolean("wysiwyg-wiki", 0) ){ cgi_redirectf("wikiedit?name=%T&wysiwyg=1", zName); }else{ cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype); } } style_header("Create A New Wiki Page"); wiki_standard_submenu(W_ALL_BUT(W_NEW)); @ <p>Rules for wiki page names:</p> well_formed_wiki_name_rules(); form_begin(0, "%R/wikinew"); @ <p>Name of new wiki page: @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br /> @ Markup style: mimetype_option_menu("text/x-fossil-wiki"); @ <br /><input type="submit" value="Create" /> @ </p></form> if( zName[0] ){ @ <p><span class="wikiError"> @ "%h(zName)" is not a valid wiki page name!</span></p> } |
︙ | ︙ | |||
555 556 557 558 559 560 561 | char *zId; zDate = db_text(0, "SELECT datetime('now')"); zRemark = PD("r",""); zUser = PD("u",g.zLogin); if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ zId = db_text(0, "SELECT lower(hex(randomblob(8)))"); | | | | | | | | | 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | char *zId; zDate = db_text(0, "SELECT datetime('now')"); zRemark = PD("r",""); zUser = PD("u",g.zLogin); if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ zId = db_text(0, "SELECT lower(hex(randomblob(8)))"); blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h", zId, zDate, login_name()); if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ blob_appendf(p, " (claiming to be %h)", zUser); } blob_appendf(p, " added:</i><br />\n%s</div id=\"%s\">", zRemark, zId); }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name()); if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ blob_appendf(p, " (claiming to be %h)", zUser); } blob_appendf(p, " added:*\n\n%s\n", zRemark); }else{ blob_appendf(p, "\n\n------------------------------------------------\n" "On %s UTC %s", zDate, login_name()); if( zUser[0] && fossil_strcmp(zUser,login_name()) ){ blob_appendf(p, " (claiming to be %s)", zUser); } blob_appendf(p, " added:\n\n%s\n", zRemark); } fossil_free(zDate); } |
︙ | ︙ | |||
599 600 601 602 603 604 605 | login_check_credentials(); zPageName = PD("name",""); zMimetype = wiki_filter_mimetypes(P("mimetype")); if( check_name(zPageName) ) return; isSandbox = is_sandbox(zPageName); if( !isSandbox ){ zTag = mprintf("wiki-%s", zPageName); | | | | | 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 | login_check_credentials(); zPageName = PD("name",""); zMimetype = wiki_filter_mimetypes(P("mimetype")); if( check_name(zPageName) ) return; isSandbox = is_sandbox(zPageName); if( !isSandbox ){ zTag = mprintf("wiki-%s", zPageName); rid = db_int(0, "SELECT rid FROM tagxref" " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" " ORDER BY mtime DESC", zTag ); free(zTag); if( !rid ){ fossil_redirect_home(); return; } } if( !g.perm.ApndWiki ){ login_needed(g.anon.ApndWiki); return; } if( P("submit")!=0 && P("r")!=0 && P("u")!=0 && (goodCaptcha = captcha_is_correct()) ){ char *zDate; Blob cksum; Blob body; Blob wiki; Manifest *pWiki = 0; blob_zero(&body); if( isSandbox ){ blob_append(&body, db_get("sandbox",""), -1); appendRemark(&body, zMimetype); db_set("sandbox", blob_str(&body), 0); }else{ login_verify_csrf_secret(); pWiki = manifest_get(rid, CFTYPE_WIKI, 0); if( pWiki ){ blob_append(&body, pWiki->zWiki, -1); |
︙ | ︙ | |||
648 649 650 651 652 653 654 | blob_appendf(&wiki, "N %s\n", zMimetype); } if( rid ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); blob_appendf(&wiki, "P %s\n", zUuid); free(zUuid); } | | | | | | 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 | blob_appendf(&wiki, "N %s\n", zMimetype); } if( rid ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); blob_appendf(&wiki, "P %s\n", zUuid); free(zUuid); } if( !login_is_nobody() ){ blob_appendf(&wiki, "U %F\n", login_name()); } appendRemark(&body, zMimetype); blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body)); md5sum_blob(&wiki, &cksum); blob_appendf(&wiki, "Z %b\n", &cksum); blob_reset(&cksum); wiki_put(&wiki, rid, wiki_need_moderation(0)); db_end_transaction(0); } cgi_redirectf("wiki?name=%T", zPageName); } if( P("cancel")!=0 ){ cgi_redirectf("wiki?name=%T", zPageName); return; } style_set_current_page("%T?name=%T", g.zPath, zPageName); style_header("Append Comment To: %s", zPageName); if( !goodCaptcha ){ @ <p class="generalError">Error: Incorrect security code.</p> } if( P("preview")!=0 ){ Blob preview; blob_zero(&preview); |
︙ | ︙ | |||
688 689 690 691 692 693 694 | login_insert_csrf_secret(); @ <input type="hidden" name="name" value="%h(zPageName)" /> @ <input type="hidden" name="mimetype" value="%h(zMimetype)" /> @ Your Name: @ <input type="text" name="u" size="20" value="%h(zUser)" /><br /> zFormat = mimetype_common_name(zMimetype); @ Comment to append (formatted as %s(zFormat)):<br /> | | | 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 | login_insert_csrf_secret(); @ <input type="hidden" name="name" value="%h(zPageName)" /> @ <input type="hidden" name="mimetype" value="%h(zMimetype)" /> @ Your Name: @ <input type="text" name="u" size="20" value="%h(zUser)" /><br /> zFormat = mimetype_common_name(zMimetype); @ Comment to append (formatted as %s(zFormat)):<br /> @ <textarea name="r" class="wikiedit" cols="80" @ rows="10" wrap="virtual">%h(PD("r",""))</textarea> @ <br /> @ <input type="submit" name="preview" value="Preview Your Comment" /> @ <input type="submit" name="submit" value="Append Your Changes" /> @ <input type="submit" name="cancel" value="Cancel" /> captcha_generate(0); @ </form> |
︙ | ︙ | |||
722 723 724 725 726 727 728 | ** WEBPAGE: whistory ** URL: /whistory?name=PAGENAME ** ** Show the complete change history for a single wiki page. */ void whistory_page(void){ Stmt q; | < < | | < < | < < | < | | < < | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 | ** WEBPAGE: whistory ** URL: /whistory?name=PAGENAME ** ** Show the complete change history for a single wiki page. */ void whistory_page(void){ Stmt q; const char *zPageName; login_check_credentials(); if( !g.perm.Hyperlink ){ login_needed(g.anon.Hyperlink); return; } zPageName = PD("name",""); style_header("History Of %s", zPageName); db_prepare(&q, "%s AND event.objid IN " " (SELECT rid FROM tagxref WHERE tagid=" "(SELECT tagid FROM tag WHERE tagname='wiki-%q')" " UNION SELECT attachid FROM attachment" " WHERE target=%Q)" "ORDER BY mtime DESC", timeline_query_for_www(), zPageName, zPageName); zWikiPageName = zPageName; www_print_timeline(&q, TIMELINE_ARTID, 0, 0, 0, wiki_history_extra); db_finalize(&q); style_footer(); } /* ** WEBPAGE: wdiff ** URL: /whistory?name=PAGENAME&a=RID1&b=RID2 ** ** Show the difference between two wiki pages. */ void wdiff_page(void){ int rid1, rid2; const char *zPageName; Manifest *pW1, *pW2 = 0; Blob w1, w2, d; u64 diffFlags; login_check_credentials(); rid1 = atoi(PD("a","0")); if( !g.perm.Hyperlink ){ login_needed(g.anon.Hyperlink); return; } if( rid1==0 ) fossil_redirect_home(); rid2 = atoi(PD("b","0")); zPageName = PD("name",""); style_header("Changes To %s", zPageName); if( rid2==0 ){ rid2 = db_int(0, "SELECT objid FROM event JOIN tagxref ON objid=rid AND tagxref.tagid=" "(SELECT tagid FROM tag WHERE tagname='wiki-%q')" " WHERE event.mtime<(SELECT mtime FROM event WHERE objid=%d)" " ORDER BY event.mtime DESC LIMIT 1", |
︙ | ︙ | |||
807 808 809 810 811 812 813 | ** ** - wiki page name ** - tagxref (whatever that really is!) ** ** Used by wcontent_page() and the JSON wiki code. */ void wiki_prepare_page_list( Stmt * pStmt ){ | | | > | > | 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 | ** ** - wiki page name ** - tagxref (whatever that really is!) ** ** Used by wcontent_page() and the JSON wiki code. */ void wiki_prepare_page_list( Stmt * pStmt ){ db_prepare(pStmt, "SELECT" " substr(tagname, 6) as name," " (SELECT value FROM tagxref WHERE tagid=tag.tagid" " ORDER BY mtime DESC) as tagXref" " FROM tag WHERE tagname GLOB 'wiki-*'" " ORDER BY lower(tagname) /*sort*/" ); } /* ** WEBPAGE: wcontent ** ** all=1 Show deleted pages ** ** List all available wiki pages with date created and last modified. */ void wcontent_page(void){ Stmt q; int showAll = P("all")!=0; login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } style_header("Available Wiki Pages"); if( showAll ){ style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop); }else{ style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop); } wiki_standard_submenu(W_ALL_BUT(W_LIST)); @ <ul> wiki_prepare_page_list(&q); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); int size = db_column_int(&q, 1); if( size>0 ){ @ <li>%z(href("%R/wiki?name=%T",zName))%h(zName)</a></li> |
︙ | ︙ | |||
858 859 860 861 862 863 864 | ** WEBPAGE: wfind ** ** URL: /wfind?title=TITLE ** List all wiki pages whose titles contain the search text */ void wfind_page(void){ Stmt q; | | | | | | 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 | ** WEBPAGE: wfind ** ** URL: /wfind?title=TITLE ** List all wiki pages whose titles contain the search text */ void wfind_page(void){ Stmt q; const char *zTitle; login_check_credentials(); if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; } zTitle = PD("title","*"); style_header("Wiki Pages Found"); @ <ul> db_prepare(&q, "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname like 'wiki-%%%q%%'" " ORDER BY lower(tagname) /*sort*/" , zTitle); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); @ <li>%z(href("%R/wiki?name=%T",zName))%h(zName)</a></li> } db_finalize(&q); @ </ul> style_footer(); |
︙ | ︙ | |||
889 890 891 892 893 894 895 | @ <h2>Formatting Rule Summary</h2> @ <ol> @ <li>Blank lines are paragraph breaks</li> @ <li>Bullets are "*" surrounded by two spaces at the beginning of the @ line.</li> @ <li>Enumeration items are "#" surrounded by two spaces at the beginning of @ a line.</li> | | | > | | > | | 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | @ <h2>Formatting Rule Summary</h2> @ <ol> @ <li>Blank lines are paragraph breaks</li> @ <li>Bullets are "*" surrounded by two spaces at the beginning of the @ line.</li> @ <li>Enumeration items are "#" surrounded by two spaces at the beginning of @ a line.</li> @ <li>Indented paragraphs begin with a tab or two spaces.</li> @ <li>Hyperlinks are contained with square brackets: "[target]" or @ "[target|name]".</li> @ <li>Most ordinary HTML works.</li> @ <li><verbatim> and <nowiki>.</li> @ </ol> @ <p>We call the first five rules above "wiki" formatting rules. The @ last two rules are the HTML formatting rule.</p> @ <h2>Formatting Rule Details</h2> @ <ol> @ <li> <p><span class="wikiruleHead">Paragraphs</span>. @ Any sequence of one or more blank lines forms @ a paragraph break. Centered or right-justified paragraphs are not @ supported by wiki markup, but you can do these things if you need them @ using HTML.</p></li> @ <li> <p><span class="wikiruleHead">Bullet Lists</span>. @ A bullet list item is a line that begins with a single "*" character @ surrounded on @ both sides by two or more spaces or by a tab. Only a single level @ of bullet list is supported by wiki. For nested lists, use HTML.</p></li> @ <li> <p><span class="wikiruleHead">Enumeration Lists</span>. @ An enumeration list item is a line that begins with a single "#" character @ surrounded on both sides by two or more spaces or by a tab. Only a single @ level of enumeration list is supported by wiki. For nested lists or for @ enumerations that count using letters or roman numerials, use HTML.</p></li> @ <li> <p><span class="wikiruleHead">Indented Paragraphs</span>. @ Any paragraph that begins with two or more spaces or a tab and @ which is not a bullet or enumeration list item is rendered @ indented. Only a single level of indentation is supported by wiki; use @ HTML for deeper indentation.</p></li> @ <li> <p><span class="wikiruleHead">Hyperlinks</span>. @ Text within square brackets ("[...]") becomes a hyperlink. The @ target can be a wiki page name, the artifact ID of a check-in or ticket, @ the name of an image, or a URL. By default, the target is displayed @ as the text of the hyperlink. But you can specify alternative text @ after the target name separated by a "|" character.</p> @ <p>You can also link to internal anchor names using [#anchor-name], @ providing @ you have added the necessary "<a name='anchor-name'></a>" @ tag to your wiki page.</p></li> @ <li> <p><span class="wikiruleHead">HTML</span>. @ The following standard HTML elements may be used: show_allowed_wiki_markup(); @ . There are two non-standard elements available: @ <verbatim> and <nowiki>. @ No other elements are allowed. All attributes are checked and |
︙ | ︙ | |||
953 954 955 956 957 958 959 960 | /* ** Add a new wiki page to the repository. The page name is ** given by the zPageName parameter. isNew must be true to create ** a new page. If no previous page with the name zPageName exists ** and isNew is false, then this routine throws an error. ** ** The content of the new page is given by the blob pContent. */ | > > > > | > | 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 | /* ** Add a new wiki page to the repository. The page name is ** given by the zPageName parameter. isNew must be true to create ** a new page. If no previous page with the name zPageName exists ** and isNew is false, then this routine throws an error. ** ** The content of the new page is given by the blob pContent. ** ** zMimeType specifies the N-card for the wiki page. If it is 0, ** empty, or "text/x-fossil-wiki" (the default format) then it is ** ignored. */ int wiki_cmd_commit(const char *zPageName, int isNew, Blob *pContent, const char *zMimeType, int localUser){ Blob wiki; /* Wiki page content */ Blob cksum; /* wiki checksum */ int rid; /* artifact ID of parent page */ char *zDate; /* timestamp */ char *zUuid; /* uuid for rid */ rid = db_int(0, |
︙ | ︙ | |||
985 986 987 988 989 990 991 992 993 994 995 996 997 | } blob_zero(&wiki); zDate = date_in_standard_format("now"); blob_appendf(&wiki, "D %s\n", zDate); free(zDate); blob_appendf(&wiki, "L %F\n", zPageName ); if( rid ){ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); blob_appendf(&wiki, "P %s\n", zUuid); free(zUuid); } user_select(); | > > > > | | | | | > > | > | | < | < | | | | > > > | > > > > > > > > > > > > | | > < | | | | | < | 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 | } blob_zero(&wiki); zDate = date_in_standard_format("now"); blob_appendf(&wiki, "D %s\n", zDate); free(zDate); blob_appendf(&wiki, "L %F\n", zPageName ); if( zMimeType && *zMimeType && 0!=fossil_strcmp(zMimeType,"text/x-fossil-wiki") ){ blob_appendf(&wiki, "N %F\n", zMimeType); } if( rid ){ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); blob_appendf(&wiki, "P %s\n", zUuid); free(zUuid); } user_select(); if( !login_is_nobody() ){ blob_appendf(&wiki, "U %F\n", login_name()); } blob_appendf( &wiki, "W %d\n%s\n", blob_size(pContent), blob_str(pContent) ); md5sum_blob(&wiki, &cksum); blob_appendf(&wiki, "Z %b\n", &cksum); blob_reset(&cksum); db_begin_transaction(); wiki_put(&wiki, 0, wiki_need_moderation(localUser)); db_end_transaction(0); return 1; } /* ** COMMAND: wiki* ** ** Usage: %fossil wiki (export|create|commit|list) WikiName ** ** Run various subcommands to work with wiki entries. ** ** %fossil wiki export PAGENAME ?FILE? ** ** Sends the latest version of the PAGENAME wiki ** entry to the given file or standard output. ** ** %fossil wiki commit PAGENAME ?FILE? [-mimetype TEXT-FORMAT] ** ** Commit changes to a wiki page from FILE or from standard ** input. The -mimetype (-M) flag specifies the mime type, ** defaulting to the type used by the previous version of ** the page or (for new pages) text/x-fossil-wiki. ** ** %fossil wiki create PAGENAME ?FILE? [-mimetype TEXT-FORMAT] ** ** Create a new wiki page with initial content taken from ** FILE or from standard input. ** ** %fossil wiki list ** %fossil wiki ls ** ** Lists all wiki entries, one per line, ordered ** case-insensitively by name. ** */ void wiki_cmd(void){ int n; db_find_and_open_repository(0, 0); if( g.argc<3 ){ goto wiki_cmd_usage; } n = strlen(g.argv[2]); if( n==0 ){ goto wiki_cmd_usage; } if( strncmp(g.argv[2],"export",n)==0 ){ const char *zPageName; /* Name of the wiki page to export */ const char *zFile; /* Name of the output file (0=stdout) */ int rid; /* Artifact ID of the wiki page */ int i; /* Loop counter */ char *zBody = 0; /* Wiki page content */ Blob body; /* Wiki page content */ Manifest *pWiki = 0; /* Parsed wiki page content */ if( (g.argc!=4) && (g.argc!=5) ){ usage("export PAGENAME ?FILE?"); } zPageName = g.argv[3]; rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" " ORDER BY x.mtime DESC LIMIT 1", zPageName ); if( (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){ zBody = pWiki->zWiki; } if( zBody==0 ){ fossil_fatal("wiki page [%s] not found",zPageName); } for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){} zBody[i] = 0; zFile = (g.argc==4) ? "-" : g.argv[4]; blob_init(&body, zBody, -1); blob_append(&body, "\n", 1); blob_write_to_file(&body, zFile); blob_reset(&body); manifest_destroy(pWiki); return; }else if( strncmp(g.argv[2],"commit",n)==0 || strncmp(g.argv[2],"create",n)==0 ){ const char *zPageName; /* page name */ Blob content; /* Input content */ int rid; Manifest *pWiki = 0; /* Parsed wiki page content */ const char *zMimeType = find_option("mimetype", "M", 1); if( g.argc!=4 && g.argc!=5 ){ usage("commit|create PAGENAME ?FILE? [-mimetype TEXT-FORMAT]"); } zPageName = g.argv[3]; if( g.argc==4 ){ blob_read_from_channel(&content, stdin, -1); }else{ blob_read_from_file(&content, g.argv[4]); } if(!zMimeType || !*zMimeType){ /* Try to deduce the mime type based on the prior version. */ rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" " ORDER BY x.mtime DESC LIMIT 1", zPageName ); if(rid>0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 && (pWiki->zMimetype && *pWiki->zMimetype)){ zMimeType = pWiki->zMimetype; } } if( g.argv[2][1]=='r' ){ wiki_cmd_commit(zPageName, 1, &content, zMimeType, 1); fossil_print("Created new wiki page %s.\n", zPageName); }else{ wiki_cmd_commit(zPageName, 0, &content, zMimeType, 1); fossil_print("Updated wiki page %s.\n", zPageName); } manifest_destroy(pWiki); blob_reset(&content); }else if( strncmp(g.argv[2],"delete",n)==0 ){ if( g.argc!=5 ){ usage("delete PAGENAME"); } fossil_fatal("delete not yet implemented."); }else if(( strncmp(g.argv[2],"list",n)==0 ) || ( strncmp(g.argv[2],"ls",n)==0 )){ Stmt q; db_prepare(&q, "SELECT substr(tagname, 6) FROM tag WHERE tagname GLOB 'wiki-*'" " ORDER BY lower(tagname) /*sort*/" ); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); fossil_print( "%s\n",zName); } db_finalize(&q); }else{ goto wiki_cmd_usage; } return; wiki_cmd_usage: usage("export|create|commit|list ..."); } |
Changes to src/wikiformat.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to do formatting of wiki text. */ | < > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code to do formatting of wiki text. */ #include "config.h" #include <assert.h> #include "wikiformat.h" #if INTERFACE /* ** Allowed wiki transformation operations */ #define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */ #define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */ #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */ #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */ #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */ #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */ #endif /* ** These are the only markup attributes allowed. */ enum allowed_attr_t { ATTR_ALIGN = 1, ATTR_ALT, ATTR_BGCOLOR, ATTR_BORDER, ATTR_CELLPADDING, ATTR_CELLSPACING, ATTR_CLASS, ATTR_CLEAR, ATTR_COLOR, ATTR_COLSPAN, ATTR_COMPACT, ATTR_FACE, ATTR_HEIGHT, ATTR_HREF, ATTR_HSPACE, ATTR_ID, ATTR_LINKS, ATTR_NAME, ATTR_ROWSPAN, ATTR_SIZE, ATTR_SRC, ATTR_START, ATTR_STYLE, ATTR_TARGET, ATTR_TYPE, ATTR_VALIGN, ATTR_VALUE, ATTR_VSPACE, ATTR_WIDTH }; enum amsk_t { AMSK_ALIGN = 0x00000001, AMSK_ALT = 0x00000002, AMSK_BGCOLOR = 0x00000004, AMSK_BORDER = 0x00000008, AMSK_CELLPADDING = 0x00000010, AMSK_CELLSPACING = 0x00000020, AMSK_CLASS = 0x00000040, AMSK_CLEAR = 0x00000080, AMSK_COLOR = 0x00000100, AMSK_COLSPAN = 0x00000200, AMSK_COMPACT = 0x00000400, /* re-use = 0x00000800, */ AMSK_FACE = 0x00001000, AMSK_HEIGHT = 0x00002000, AMSK_HREF = 0x00004000, AMSK_HSPACE = 0x00008000, AMSK_ID = 0x00010000, AMSK_LINKS = 0x00020000, AMSK_NAME = 0x00040000, AMSK_ROWSPAN = 0x00080000, AMSK_SIZE = 0x00100000, AMSK_SRC = 0x00200000, AMSK_START = 0x00400000, AMSK_STYLE = 0x00800000, AMSK_TARGET = 0x01000000, AMSK_TYPE = 0x02000000, AMSK_VALIGN = 0x04000000, AMSK_VALUE = 0x08000000, AMSK_VSPACE = 0x10000000, AMSK_WIDTH = 0x20000000 }; static const struct AllowedAttribute { const char *zName; unsigned int iMask; } aAttribute[] = { /* These indexes MUST line up with their corresponding allowed_attr_t enum values. */ { 0, 0 }, { "align", AMSK_ALIGN }, { "alt", AMSK_ALT }, { "bgcolor", AMSK_BGCOLOR }, { "border", AMSK_BORDER }, { "cellpadding", AMSK_CELLPADDING }, { "cellspacing", AMSK_CELLSPACING }, { "class", AMSK_CLASS }, { "clear", AMSK_CLEAR }, { "color", AMSK_COLOR }, { "colspan", AMSK_COLSPAN }, { "compact", AMSK_COMPACT }, { "face", AMSK_FACE }, { "height", AMSK_HEIGHT }, { "href", AMSK_HREF }, { "hspace", AMSK_HSPACE }, { "id", AMSK_ID }, { "links", AMSK_LINKS }, { "name", AMSK_NAME }, { "rowspan", AMSK_ROWSPAN }, { "size", AMSK_SIZE }, { "src", AMSK_SRC }, { "start", AMSK_START }, { "style", AMSK_STYLE }, { "target", AMSK_TARGET }, { "type", AMSK_TYPE }, { "valign", AMSK_VALIGN }, { "value", AMSK_VALUE }, { "vspace", AMSK_VSPACE }, { "width", AMSK_WIDTH }, }; /* ** Use binary search to locate a tag in the aAttribute[] table. */ static int findAttr(const char *z){ int i, c, first, last; |
︙ | ︙ | |||
162 163 164 165 166 167 168 | ** and in numerical sequence. The first markup type must be zero. ** The value for MARKUP_XYZ must correspond to the <xyz> entry ** in aAllowedMarkup[]. */ #define MARKUP_INVALID 0 #define MARKUP_A 1 #define MARKUP_ADDRESS 2 | > > | | | | | | | | | | | | | | | | > | | | | | | > | | | | | > | | | | | | | > | | | | | | | | | | | | > | | | | | | | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | ** and in numerical sequence. The first markup type must be zero. ** The value for MARKUP_XYZ must correspond to the <xyz> entry ** in aAllowedMarkup[]. */ #define MARKUP_INVALID 0 #define MARKUP_A 1 #define MARKUP_ADDRESS 2 #define MARKUP_HTML5_ARTICLE 3 #define MARKUP_HTML5_ASIDE 4 #define MARKUP_B 5 #define MARKUP_BIG 6 #define MARKUP_BLOCKQUOTE 7 #define MARKUP_BR 8 #define MARKUP_CENTER 9 #define MARKUP_CITE 10 #define MARKUP_CODE 11 #define MARKUP_COL 12 #define MARKUP_COLGROUP 13 #define MARKUP_DD 14 #define MARKUP_DFN 15 #define MARKUP_DIV 16 #define MARKUP_DL 17 #define MARKUP_DT 18 #define MARKUP_EM 19 #define MARKUP_FONT 20 #define MARKUP_HTML5_FOOTER 21 #define MARKUP_H1 22 #define MARKUP_H2 23 #define MARKUP_H3 24 #define MARKUP_H4 25 #define MARKUP_H5 26 #define MARKUP_H6 27 #define MARKUP_HTML5_HEADER 28 #define MARKUP_HR 29 #define MARKUP_I 30 #define MARKUP_IMG 31 #define MARKUP_KBD 32 #define MARKUP_LI 33 #define MARKUP_HTML5_NAV 34 #define MARKUP_NOBR 35 #define MARKUP_NOWIKI 36 #define MARKUP_OL 37 #define MARKUP_P 38 #define MARKUP_PRE 39 #define MARKUP_S 40 #define MARKUP_SAMP 41 #define MARKUP_HTML5_SECTION 42 #define MARKUP_SMALL 43 #define MARKUP_SPAN 44 #define MARKUP_STRIKE 45 #define MARKUP_STRONG 46 #define MARKUP_SUB 47 #define MARKUP_SUP 48 #define MARKUP_TABLE 49 #define MARKUP_TBODY 50 #define MARKUP_TD 51 #define MARKUP_TFOOT 52 #define MARKUP_TH 53 #define MARKUP_THEAD 54 #define MARKUP_TITLE 55 #define MARKUP_TR 56 #define MARKUP_TT 57 #define MARKUP_U 58 #define MARKUP_UL 59 #define MARKUP_VAR 60 #define MARKUP_VERBATIM 61 /* ** The various markup is divided into the following types: */ #define MUTYPE_SINGLE 0x0001 /* <img>, <br>, or <hr> */ #define MUTYPE_BLOCK 0x0002 /* Forms a new paragraph. ex: <p>, <h2> */ #define MUTYPE_FONT 0x0004 /* Font changes. ex: <b>, <font>, <sub> */ |
︙ | ︙ | |||
249 250 251 252 253 254 255 256 257 258 259 260 261 262 | short int iType; /* The MUTYPE_* code */ int allowedAttr; /* Allowed attributes on this markup */ } aMarkup[] = { { 0, MARKUP_INVALID, 0, 0 }, { "a", MARKUP_A, MUTYPE_HYPERLINK, AMSK_HREF|AMSK_NAME|AMSK_CLASS|AMSK_TARGET|AMSK_STYLE }, { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, AMSK_STYLE }, { "b", MARKUP_B, MUTYPE_FONT, AMSK_STYLE }, { "big", MARKUP_BIG, MUTYPE_FONT, AMSK_STYLE }, { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, AMSK_STYLE }, { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR }, { "center", MARKUP_CENTER, MUTYPE_BLOCK, AMSK_STYLE }, { "cite", MARKUP_CITE, MUTYPE_FONT, AMSK_STYLE }, { "code", MARKUP_CODE, MUTYPE_FONT, AMSK_STYLE }, | > > > > > | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | short int iType; /* The MUTYPE_* code */ int allowedAttr; /* Allowed attributes on this markup */ } aMarkup[] = { { 0, MARKUP_INVALID, 0, 0 }, { "a", MARKUP_A, MUTYPE_HYPERLINK, AMSK_HREF|AMSK_NAME|AMSK_CLASS|AMSK_TARGET|AMSK_STYLE }, { "address", MARKUP_ADDRESS, MUTYPE_BLOCK, AMSK_STYLE }, { "article", MARKUP_HTML5_ARTICLE, MUTYPE_BLOCK, AMSK_ID|AMSK_CLASS|AMSK_STYLE }, { "aside", MARKUP_HTML5_ASIDE, MUTYPE_BLOCK, AMSK_ID|AMSK_CLASS|AMSK_STYLE }, { "b", MARKUP_B, MUTYPE_FONT, AMSK_STYLE }, { "big", MARKUP_BIG, MUTYPE_FONT, AMSK_STYLE }, { "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, AMSK_STYLE }, { "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR }, { "center", MARKUP_CENTER, MUTYPE_BLOCK, AMSK_STYLE }, { "cite", MARKUP_CITE, MUTYPE_FONT, AMSK_STYLE }, { "code", MARKUP_CODE, MUTYPE_FONT, AMSK_STYLE }, |
︙ | ︙ | |||
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, { "dl", MARKUP_DL, MUTYPE_LIST, AMSK_COMPACT|AMSK_STYLE }, { "dt", MARKUP_DT, MUTYPE_LI, AMSK_STYLE }, { "em", MARKUP_EM, MUTYPE_FONT, AMSK_STYLE }, { "font", MARKUP_FONT, MUTYPE_FONT, AMSK_COLOR|AMSK_FACE|AMSK_SIZE|AMSK_STYLE }, { "h1", MARKUP_H1, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h2", MARKUP_H2, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h3", MARKUP_H3, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h4", MARKUP_H4, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h5", MARKUP_H5, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h6", MARKUP_H6, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "hr", MARKUP_HR, MUTYPE_SINGLE, AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH| AMSK_STYLE|AMSK_CLASS }, { "i", MARKUP_I, MUTYPE_FONT, AMSK_STYLE }, { "img", MARKUP_IMG, MUTYPE_SINGLE, AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT| AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH|AMSK_STYLE }, { "kbd", MARKUP_KBD, MUTYPE_FONT, AMSK_STYLE }, { "li", MARKUP_LI, MUTYPE_LI, AMSK_TYPE|AMSK_VALUE|AMSK_STYLE }, { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 }, { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 }, { "ol", MARKUP_OL, MUTYPE_LIST, AMSK_START|AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE }, { "p", MARKUP_P, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "pre", MARKUP_PRE, MUTYPE_BLOCK, AMSK_STYLE }, { "s", MARKUP_S, MUTYPE_FONT, AMSK_STYLE }, { "samp", MARKUP_SAMP, MUTYPE_FONT, AMSK_STYLE }, { "small", MARKUP_SMALL, MUTYPE_FONT, AMSK_STYLE }, { "span", MARKUP_SPAN, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "strike", MARKUP_STRIKE, MUTYPE_FONT, AMSK_STYLE }, { "strong", MARKUP_STRONG, MUTYPE_FONT, AMSK_STYLE }, { "sub", MARKUP_SUB, MUTYPE_FONT, AMSK_STYLE }, { "sup", MARKUP_SUP, MUTYPE_FONT, AMSK_STYLE }, | > > > > > > > > > > > | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | AMSK_ID|AMSK_CLASS|AMSK_STYLE }, { "dl", MARKUP_DL, MUTYPE_LIST, AMSK_COMPACT|AMSK_STYLE }, { "dt", MARKUP_DT, MUTYPE_LI, AMSK_STYLE }, { "em", MARKUP_EM, MUTYPE_FONT, AMSK_STYLE }, { "font", MARKUP_FONT, MUTYPE_FONT, AMSK_COLOR|AMSK_FACE|AMSK_SIZE|AMSK_STYLE }, { "footer", MARKUP_HTML5_FOOTER, MUTYPE_BLOCK, AMSK_ID|AMSK_CLASS|AMSK_STYLE }, { "h1", MARKUP_H1, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h2", MARKUP_H2, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h3", MARKUP_H3, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h4", MARKUP_H4, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h5", MARKUP_H5, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "h6", MARKUP_H6, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "header", MARKUP_HTML5_HEADER, MUTYPE_BLOCK, AMSK_ID|AMSK_CLASS|AMSK_STYLE }, { "hr", MARKUP_HR, MUTYPE_SINGLE, AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH| AMSK_STYLE|AMSK_CLASS }, { "i", MARKUP_I, MUTYPE_FONT, AMSK_STYLE }, { "img", MARKUP_IMG, MUTYPE_SINGLE, AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT| AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH|AMSK_STYLE }, { "kbd", MARKUP_KBD, MUTYPE_FONT, AMSK_STYLE }, { "li", MARKUP_LI, MUTYPE_LI, AMSK_TYPE|AMSK_VALUE|AMSK_STYLE }, { "nav", MARKUP_HTML5_NAV, MUTYPE_BLOCK, AMSK_ID|AMSK_CLASS|AMSK_STYLE }, { "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 }, { "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 }, { "ol", MARKUP_OL, MUTYPE_LIST, AMSK_START|AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE }, { "p", MARKUP_P, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "pre", MARKUP_PRE, MUTYPE_BLOCK, AMSK_STYLE }, { "s", MARKUP_S, MUTYPE_FONT, AMSK_STYLE }, { "samp", MARKUP_SAMP, MUTYPE_FONT, AMSK_STYLE }, { "section", MARKUP_HTML5_SECTION, MUTYPE_BLOCK, AMSK_ID|AMSK_CLASS|AMSK_STYLE }, { "small", MARKUP_SMALL, MUTYPE_FONT, AMSK_STYLE }, { "span", MARKUP_SPAN, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "strike", MARKUP_STRIKE, MUTYPE_FONT, AMSK_STYLE }, { "strong", MARKUP_STRONG, MUTYPE_FONT, AMSK_STYLE }, { "sub", MARKUP_SUB, MUTYPE_FONT, AMSK_STYLE }, { "sup", MARKUP_SUP, MUTYPE_FONT, AMSK_STYLE }, |
︙ | ︙ | |||
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "th", MARKUP_TH, MUTYPE_TD, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, { "thead", MARKUP_THEAD, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "tr", MARKUP_TR, MUTYPE_TR, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, { "tt", MARKUP_TT, MUTYPE_FONT, AMSK_STYLE }, { "u", MARKUP_U, MUTYPE_FONT, AMSK_STYLE }, { "ul", MARKUP_UL, MUTYPE_LIST, AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE }, { "var", MARKUP_VAR, MUTYPE_FONT, AMSK_STYLE }, { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_ID|AMSK_TYPE }, }; void show_allowed_wiki_markup( void ){ int i; /* loop over allowedAttr */ | > < | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "th", MARKUP_TH, MUTYPE_TD, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, { "thead", MARKUP_THEAD, MUTYPE_BLOCK, AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, { "title", MARKUP_TITLE, MUTYPE_BLOCK, 0 }, { "tr", MARKUP_TR, MUTYPE_TR, AMSK_ALIGN|AMSK_BGCOLOR|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, { "tt", MARKUP_TT, MUTYPE_FONT, AMSK_STYLE }, { "u", MARKUP_U, MUTYPE_FONT, AMSK_STYLE }, { "ul", MARKUP_UL, MUTYPE_LIST, AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE }, { "var", MARKUP_VAR, MUTYPE_FONT, AMSK_STYLE }, { "verbatim", MARKUP_VERBATIM, MUTYPE_SPECIAL, AMSK_ID|AMSK_TYPE }, }; void show_allowed_wiki_markup( void ){ int i; /* loop over allowedAttr */ for( i=1 ; i<=sizeof(aMarkup)/sizeof(aMarkup[0]) - 1 ; i++ ){ @ <%s(aMarkup[i].zName)> } } /* ** Use binary search to locate a tag in the aMarkup[] table. |
︙ | ︙ | |||
728 729 730 731 732 733 734 | /* ** z[] is an HTML markup element - something that begins with '<'. ** Parse this element into the p structure. ** ** The content of z[] might be modified by converting characters ** to lowercase and by inserting some "\000" characters. */ | | | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 | /* ** z[] is an HTML markup element - something that begins with '<'. ** Parse this element into the p structure. ** ** The content of z[] might be modified by converting characters ** to lowercase and by inserting some "\000" characters. */ static int parseMarkup(ParsedMarkup *p, char *z){ int i, j, c; int iACode; char *zValue; int seen = 0; char zTag[100]; if( z[1]=='/' ){ |
︙ | ︙ | |||
760 761 762 763 764 765 766 | p->aAttr[0].iACode = iACode = ATTR_ID; i++; p->aAttr[0].zValue = &z[i]; while( fossil_isalnum(z[i]) ){ i++; } p->aAttr[0].cTerm = c = z[i]; z[i++] = 0; p->nAttr = 1; | | | | 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 | p->aAttr[0].iACode = iACode = ATTR_ID; i++; p->aAttr[0].zValue = &z[i]; while( fossil_isalnum(z[i]) ){ i++; } p->aAttr[0].cTerm = c = z[i]; z[i++] = 0; p->nAttr = 1; if( c=='>' ) return 0; } while( fossil_isspace(z[i]) ){ i++; } while( c!='>' && p->nAttr<8 && fossil_isalpha(z[i]) ){ int attrOk; /* True to preserve attribute. False to ignore it */ j = 0; while( fossil_isalnum(z[i]) ){ if( j<sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]); i++; } zTag[j] = 0; p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag); |
︙ | ︙ | |||
807 808 809 810 811 812 813 814 815 816 817 818 819 820 | if( attrOk ){ seen |= aAttribute[iACode].iMask; p->nAttr++; } while( fossil_isspace(z[i]) ){ i++; } if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; } } /* ** Render markup on the given blob. */ static void renderMarkup(Blob *pOut, ParsedMarkup *p){ int i; | > | 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 | if( attrOk ){ seen |= aAttribute[iACode].iMask; p->nAttr++; } while( fossil_isspace(z[i]) ){ i++; } if( z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; } return seen; } /* ** Render markup on the given blob. */ static void renderMarkup(Blob *pOut, ParsedMarkup *p){ int i; |
︙ | ︙ | |||
846 847 848 849 850 851 852 | ** original content. */ static void unparseMarkup(ParsedMarkup *p){ int i, n; for(i=0; i<p->nAttr; i++){ char *z = p->aAttr[i].zValue; if( z==0 ) continue; | > | | > | 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 | ** original content. */ static void unparseMarkup(ParsedMarkup *p){ int i, n; for(i=0; i<p->nAttr; i++){ char *z = p->aAttr[i].zValue; if( z==0 ) continue; if( p->aAttr[i].cTerm ){ n = strlen(z); z[n] = p->aAttr[i].cTerm; } } } /* ** Return the value of attribute attrId. Return NULL if there is no ** ID attribute. */ |
︙ | ︙ | |||
924 925 926 927 928 929 930 | ** output its end tag if it is not a </div> tag. */ static void popStack(Renderer *p){ if( p->nStack ){ int iCode; p->nStack--; iCode = p->aStack[p->nStack].iCode; | | | 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 | ** output its end tag if it is not a </div> tag. */ static void popStack(Renderer *p){ if( p->nStack ){ int iCode; p->nStack--; iCode = p->aStack[p->nStack].iCode; if( (iCode!=MARKUP_DIV || p->aStack[p->nStack].zId==0) && p->pOut ){ blob_appendf(p->pOut, "</%s>", aMarkup[iCode].zName); } } } /* ** Push a new markup value onto the stack. Enlarge the stack |
︙ | ︙ | |||
1010 1011 1012 1013 1014 1015 1016 | /* ** Begin a new paragraph if that something that is needed. */ static void startAutoParagraph(Renderer *p){ if( p->wantAutoParagraph==0 ) return; if( p->state & WIKI_LINKSONLY ) return; if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return; | | | 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 | /* ** Begin a new paragraph if that something that is needed. */ static void startAutoParagraph(Renderer *p){ if( p->wantAutoParagraph==0 ) return; if( p->state & WIKI_LINKSONLY ) return; if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return; blob_append(p->pOut, "<p>", -1); p->wantAutoParagraph = 0; p->inAutoParagraph = 1; } /* ** End a paragraph if we are in one. */ |
︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 | memcpy(zUpper, zLower, n+1); zUpper[n-1]++; if( once ){ const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'"); db_static_prepare(&q, "SELECT %s FROM ticket " " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr", | | | 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 | memcpy(zUpper, zLower, n+1); zUpper[n-1]++; if( once ){ const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'"); db_static_prepare(&q, "SELECT %s FROM ticket " " WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr", zClosedExpr /*safe-for-%s*/ ); once = 0; } db_bind_text(&q, ":lwr", zLower); db_bind_text(&q, ":upr", zUpper); if( db_step(&q)==SQLITE_ROW ){ rc = 1; |
︙ | ︙ | |||
1170 1171 1172 1173 1174 1175 1176 | if( strncmp(zTarget, "http:", 5)==0 || strncmp(zTarget, "https:", 6)==0 || strncmp(zTarget, "ftp:", 4)==0 || strncmp(zTarget, "mailto:", 7)==0 ){ blob_appendf(p->pOut, "<a href=\"%s\">", zTarget); }else if( zTarget[0]=='/' ){ | | | 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 | if( strncmp(zTarget, "http:", 5)==0 || strncmp(zTarget, "https:", 6)==0 || strncmp(zTarget, "ftp:", 4)==0 || strncmp(zTarget, "mailto:", 7)==0 ){ blob_appendf(p->pOut, "<a href=\"%s\">", zTarget); }else if( zTarget[0]=='/' ){ blob_appendf(p->pOut, "<a href=\"%R%h\">", zTarget); }else if( zTarget[0]=='.' && (zTarget[1]=='/' || (zTarget[1]=='.' && zTarget[2]=='/')) && (p->state & WIKI_LINKSONLY)==0 ){ blob_appendf(p->pOut, "<a href=\"%h\">", zTarget); }else if( zTarget[0]=='#' ){ blob_appendf(p->pOut, "<a href=\"%h\">", zTarget); }else if( is_valid_uuid(zTarget) ){ |
︙ | ︙ | |||
1207 1208 1209 1210 1211 1212 1213 | zTerm = "]"; } } }else if( !in_this_repo(zTarget) ){ if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){ zTerm = ""; }else{ | | > > | 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 | zTerm = "]"; } } }else if( !in_this_repo(zTarget) ){ if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){ zTerm = ""; }else{ blob_appendf(p->pOut, "<span class=\"brokenlink\">["); zTerm = "]</span>"; } }else if( g.perm.Hyperlink ){ blob_appendf(p->pOut, "%z[",href("%R/info/%s", zTarget)); zTerm = "]</a>"; }else{ zTerm = ""; } }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget); }else if( (z = validWikiPageName(p, zTarget))!=0 ){ blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", z); }else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){ |
︙ | ︙ | |||
1292 1293 1294 1295 1296 1297 1298 | blob_append(p->pOut, " ", -1); }else{ if( p->wikiList ){ popStackToTag(p, p->wikiList); p->wikiList = 0; } endAutoParagraph(p); | | | 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 | blob_append(p->pOut, " ", -1); }else{ if( p->wikiList ){ popStackToTag(p, p->wikiList); p->wikiList = 0; } endAutoParagraph(p); blob_append(p->pOut, "\n\n", 1); p->wantAutoParagraph = 1; } p->state |= AT_PARAGRAPH|AT_NEWLINE; break; } case TOKEN_NEWLINE: { blob_append(p->pOut, "\n", 1); |
︙ | ︙ | |||
1444 1445 1446 1447 1448 1449 1450 | blob_append(p->pOut, z, n); } break; } case TOKEN_MARKUP: { const char *zId; int iDiv; | | > > > > > > > > > | 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 | blob_append(p->pOut, z, n); } break; } case TOKEN_MARKUP: { const char *zId; int iDiv; int mAttr = parseMarkup(&markup, z); /* Convert <title> to <h1 align='center'> */ if( markup.iCode==MARKUP_TITLE && !p->inVerbatim ){ markup.iCode = MARKUP_H1; markup.nAttr = 1; markup.aAttr[0].iACode = AMSK_ALIGN; markup.aAttr[0].zValue = "center"; markup.aAttr[0].cTerm = 0; } /* Markup of the form </div id=ID> where there is a matching ** ID somewhere on the stack. Exit any contained verbatim. ** Pop the stack up to the matching <div>. Discard the </div> */ if( markup.iCode==MARKUP_DIV && markup.endTag && (zId = markupId(&markup))!=0 && |
︙ | ︙ | |||
1522 1523 1524 1525 1526 1527 1528 | /* Generate end-tags */ if( markup.endTag ){ popStackToTag(p, markup.iCode); }else /* Push <div> markup onto the stack together with the id=ID attribute. */ | | | 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 | /* Generate end-tags */ if( markup.endTag ){ popStackToTag(p, markup.iCode); }else /* Push <div> markup onto the stack together with the id=ID attribute. */ if( markup.iCode==MARKUP_DIV && (mAttr & ATTR_ID)!=0 ){ pushStackWithId(p, markup.iCode, markupId(&markup), (p->state & ALLOW_WIKI)!=0); }else /* Enter <verbatim> processing. With verbatim enabled, all other ** markup other than the corresponding end-tag with the same ID is ** ignored. |
︙ | ︙ | |||
1716 1717 1718 1719 1720 1721 1722 | int iStart; blob_to_utf8_no_bom(pIn, 0); z = blob_str(pIn); for(i=0; fossil_isspace(z[i]); i++){} if( z[i]!='<' ) return 0; i++; if( strncmp(&z[i],"title>", 6)!=0 ) return 0; | | > > > | > > | > > > | 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 | int iStart; blob_to_utf8_no_bom(pIn, 0); z = blob_str(pIn); for(i=0; fossil_isspace(z[i]); i++){} if( z[i]!='<' ) return 0; i++; if( strncmp(&z[i],"title>", 6)!=0 ) return 0; for(iStart=i+6; fossil_isspace(z[iStart]); iStart++){} for(i=iStart; z[i] && (z[i]!='<' || strncmp(&z[i],"</title>",8)!=0); i++){} if( strncmp(&z[i],"</title>",8)!=0 ){ blob_init(pTitle, 0, 0); blob_init(pTail, &z[iStart], -1); return 1; } if( i-iStart>0 ){ blob_init(pTitle, &z[iStart], i-iStart); }else{ blob_init(pTitle, 0, 0); } blob_init(pTail, &z[i+8], -1); return 1; } /* ** Parse text looking for wiki hyperlinks in one of the formats: ** |
︙ | ︙ | |||
1912 1913 1914 1915 1916 1917 1918 | ** Get the next HTML token. ** ** z points to the start of a token. Return the number of ** characters in that token. */ static int nextHtmlToken(const char *z){ int n; | > | | > > > > > | > > > | 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 | ** Get the next HTML token. ** ** z points to the start of a token. Return the number of ** characters in that token. */ static int nextHtmlToken(const char *z){ int n; char c; if( (c=z[0])=='<' ){ n = markupLength(z); if( n<=0 ) n = 1; }else if( fossil_isspace(c) ){ for(n=1; z[n] && fossil_isspace(z[n]); n++){} }else if( c=='&' ){ n = z[1]=='#' ? 2 : 1; while( fossil_isalnum(z[n]) ) n++; if( z[n]==';' ) n++; }else{ n = 1; for(n=1; 1; n++){ if( (c = z[n]) > '<' ) continue; if( c=='<' || c=='&' || fossil_isspace(c) || c==0 ) break; } } return n; } /* ** Attempt to reformat messy HTML to be easily readable by humans. ** |
︙ | ︙ | |||
2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 | int i; for(i=2; i<g.argc; i++){ blob_read_from_file(&in, g.argv[i]); blob_zero(&out); htmlTidy(blob_str(&in), &out); blob_reset(&in); fossil_puts(blob_str(&out), 0); blob_reset(&out); } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 | int i; for(i=2; i<g.argc; i++){ blob_read_from_file(&in, g.argv[i]); blob_zero(&out); htmlTidy(blob_str(&in), &out); blob_reset(&in); fossil_puts(blob_str(&out), 0); blob_reset(&out); } } /* ** Remove all HTML markup from the input text. The output written into ** pOut is pure text. ** ** Put the title on the first line, if there is any <title> markup. ** If there is no <title>, then create a blank first line. */ void html_to_plaintext(const char *zIn, Blob *pOut){ int n; int i, j; int inTitle = 0; /* True between <title>...</title> */ int seenText = 0; /* True after first non-whitespace seen */ int nNL = 0; /* Number of \n characters at the end of pOut */ int nWS = 0; /* True if pOut ends with whitespace */ while( fossil_isspace(zIn[0]) ) zIn++; while( zIn[0] ){ n = nextHtmlToken(zIn); if( zIn[0]=='<' && n>1 ){ int isCloseTag; int eTag; int eType; char zTag[32]; isCloseTag = zIn[1]=='/'; for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){ zTag[i] = fossil_tolower(zIn[j]); } zTag[i] = 0; eTag = findTag(zTag); eType = aMarkup[eTag].iType; if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){ zIn += n; while( zIn[0] ){ n = nextHtmlToken(zIn); if( fossil_strnicmp(zIn, "</style",7)==0 ) break; zIn += n; } if( zIn[0]=='<' ) zIn += n; continue; } if( eTag==MARKUP_TITLE ){ inTitle = !isCloseTag; } if( !isCloseTag && seenText && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){ if( nNL==0 ){ blob_append(pOut, "\n", 1); nNL++; } nWS = 1; } }else if( fossil_isspace(zIn[0]) ){ if( seenText ){ nNL = 0; if( !inTitle ){ /* '\n' -> ' ' within <title> */ for(i=0; i<n; i++) if( zIn[i]=='\n' ) nNL++; } if( !nWS ){ blob_append(pOut, nNL ? "\n" : " ", 1); nWS = 1; } } }else if( zIn[0]=='&' ){ char c = '?'; if( zIn[1]=='#' ){ int x = atoi(&zIn[1]); if( x>0 && x<=127 ) c = x; }else{ static const struct { int n; char c; char *z; } aEntity[] = { { 5, '&', "&" }, { 4, '<', "<" }, { 4, '>', ">" }, { 6, ' ', " " }, }; int jj; for(jj=0; jj<ArraySize(aEntity); jj++){ if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ c = aEntity[jj].c; break; } } } if( fossil_isspace(c) ){ if( nWS==0 && seenText ) blob_append(pOut, &c, 1); nWS = 1; nNL = c=='\n'; }else{ if( !seenText && !inTitle ) blob_append(pOut, "\n", 1); seenText = 1; nNL = nWS = 0; blob_append(pOut, &c, 1); } }else{ if( !seenText && !inTitle ) blob_append(pOut, "\n", 1); seenText = 1; nNL = nWS = 0; blob_append(pOut, zIn, n); } zIn += n; } if( nNL==0 ) blob_append(pOut, "\n", 1); } /* ** COMMAND: test-html-to-text */ void test_html_to_text(void){ Blob in, out; int i; for(i=2; i<g.argc; i++){ blob_read_from_file(&in, g.argv[i]); blob_zero(&out); html_to_plaintext(blob_str(&in), &out); blob_reset(&in); fossil_puts(blob_str(&out), 0); blob_reset(&out); } } |
Added src/winfile.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | /* ** Copyright (c) 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file implements several non-trivial file handling wrapper functions ** on Windows using the Win32 API. */ #include "config.h" #ifdef _WIN32 /* This code is for win32 only */ #include <sys/stat.h> #include <windows.h> #include "winfile.h" #ifndef LABEL_SECURITY_INFORMATION # define LABEL_SECURITY_INFORMATION (0x00000010L) #endif /* ** Fill stat buf with information received from stat() or lstat(). ** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on. ** */ int win32_stat(const wchar_t *zFilename, struct fossilStat *buf, int isWd){ WIN32_FILE_ATTRIBUTE_DATA attr; int rc = GetFileAttributesExW(zFilename, GetFileExInfoStandard, &attr); if( rc ){ ULARGE_INTEGER ull; ull.LowPart = attr.ftLastWriteTime.dwLowDateTime; ull.HighPart = attr.ftLastWriteTime.dwHighDateTime; buf->st_mode = (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR : S_IFREG; buf->st_size = (((i64)attr.nFileSizeHigh)<<32) | attr.nFileSizeLow; buf->st_mtime = ull.QuadPart / 10000000ULL - 11644473600ULL; } return !rc; } /* ** Wrapper around the access() system call. This code was copied from Tcl ** 8.6 and then modified. */ int win32_access(const wchar_t *zFilename, int flags){ int rc = 0; PSECURITY_DESCRIPTOR pSd = NULL; unsigned long size = 0; PSID pSid = NULL; BOOL sidDefaulted; BOOL impersonated = FALSE; SID_IDENTIFIER_AUTHORITY unmapped = {{0, 0, 0, 0, 0, 22}}; GENERIC_MAPPING genMap; HANDLE hToken = NULL; DWORD desiredAccess = 0, grantedAccess = 0; BOOL accessYesNo = FALSE; PPRIVILEGE_SET pPrivSet = NULL; DWORD privSetSize = 0; DWORD attr = GetFileAttributesW(zFilename); if( attr==INVALID_FILE_ATTRIBUTES ){ /* * File might not exist. */ if( GetLastError()!=ERROR_SHARING_VIOLATION ){ rc = -1; goto done; } } if( flags==F_OK ){ /* * File exists, nothing else to check. */ goto done; } if( (flags & W_OK) && (attr & FILE_ATTRIBUTE_READONLY) && !(attr & FILE_ATTRIBUTE_DIRECTORY) ){ /* * The attributes say the file is not writable. If the file is a * regular file (i.e., not a directory), then the file is not * writable, full stop. For directories, the read-only bit is * (mostly) ignored by Windows, so we can't ascertain anything about * directory access from the attrib data. */ rc = -1; goto done; } /* * It looks as if the permissions are ok, but if we are on NT, 2000 or XP, * we have a more complex permissions structure so we try to check that. * The code below is remarkably complex for such a simple thing as finding * what permissions the OS has set for a file. */ /* * First find out how big the buffer needs to be. */ GetFileSecurityW(zFilename, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, 0, 0, &size); /* * Should have failed with ERROR_INSUFFICIENT_BUFFER */ if( GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){ /* * Most likely case is ERROR_ACCESS_DENIED, which we will convert to * EACCES - just what we want! */ rc = -1; goto done; } /* * Now size contains the size of buffer needed. */ pSd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(), 0, size); if( pSd==NULL ){ rc = -1; goto done; } /* * Call GetFileSecurity() for real. */ if( !GetFileSecurityW(zFilename, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, pSd, size, &size) ){ /* * Error getting owner SD */ rc = -1; goto done; } /* * As of Samba 3.0.23 (10-Jul-2006), unmapped users and groups are * assigned to SID domains S-1-22-1 and S-1-22-2, where "22" is the * top-level authority. If the file owner and group is unmapped then * the ACL access check below will only test against world access, * which is likely to be more restrictive than the actual access * restrictions. Since the ACL tests are more likely wrong than * right, skip them. Moreover, the unix owner access permissions are * usually mapped to the Windows attributes, so if the user is the * file owner then the attrib checks above are correct (as far as they * go). */ if( !GetSecurityDescriptorOwner(pSd, &pSid, &sidDefaulted) || memcmp(GetSidIdentifierAuthority(pSid), &unmapped, sizeof(SID_IDENTIFIER_AUTHORITY))==0 ){ goto done; /* Attrib tests say access allowed. */ } /* * Perform security impersonation of the user and open the resulting * thread token. */ if( !ImpersonateSelf(SecurityImpersonation) ){ /* * Unable to perform security impersonation. */ rc = -1; goto done; } impersonated = TRUE; if( !OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE | TOKEN_QUERY, FALSE, &hToken) ){ /* * Unable to get current thread's token. */ rc = -1; goto done; } /* * Setup desiredAccess according to the access priveleges we are * checking. */ if( flags & R_OK ){ desiredAccess |= FILE_GENERIC_READ; } if( flags & W_OK){ desiredAccess |= FILE_GENERIC_WRITE; } memset(&genMap, 0, sizeof(GENERIC_MAPPING)); genMap.GenericRead = FILE_GENERIC_READ; genMap.GenericWrite = FILE_GENERIC_WRITE; genMap.GenericExecute = FILE_GENERIC_EXECUTE; genMap.GenericAll = FILE_ALL_ACCESS; AccessCheck(pSd, hToken, desiredAccess, &genMap, 0, &privSetSize, &grantedAccess, &accessYesNo); /* * Should have failed with ERROR_INSUFFICIENT_BUFFER */ if( GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){ rc = -1; goto done; } pPrivSet = (PPRIVILEGE_SET)HeapAlloc(GetProcessHeap(), 0, privSetSize); if( pPrivSet==NULL ){ rc = -1; goto done; } /* * Perform access check using the token. */ if( !AccessCheck(pSd, hToken, desiredAccess, &genMap, pPrivSet, &privSetSize, &grantedAccess, &accessYesNo) ){ /* * Unable to perform access check. */ rc = -1; goto done; } if( !accessYesNo ) rc = -1; done: if( hToken != NULL ){ CloseHandle(hToken); } if( impersonated ){ RevertToSelf(); impersonated = FALSE; } if( pPrivSet!=NULL ){ HeapFree(GetProcessHeap(), 0, pPrivSet); } if( pSd!=NULL ){ HeapFree(GetProcessHeap(), 0, pSd); } return rc; } /* ** Wrapper around the chdir() system call. */ int win32_chdir(const wchar_t *zChDir, int bChroot){ int rc = (int)!SetCurrentDirectoryW(zChDir); return rc; } /* ** Get the current working directory. ** ** On windows, the name is converted from unicode to UTF8 and all '\\' ** characters are converted to '/'. */ void win32_getcwd(char *zBuf, int nBuf){ int i; char *zUtf8; wchar_t *zWide = fossil_malloc( sizeof(wchar_t)*nBuf ); if( GetCurrentDirectoryW(nBuf, zWide)==0 ){ fossil_fatal("cannot find current working directory."); } zUtf8 = fossil_filename_to_utf8(zWide); fossil_free(zWide); for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/'; strncpy(zBuf, zUtf8, nBuf); fossil_filename_free(zUtf8); } #endif /* _WIN32 -- This code is for win32 only */ |
Changes to src/winhttp.c.
︙ | ︙ | |||
30 31 32 33 34 35 36 37 38 39 40 41 42 43 | ** HTTP request. */ typedef struct HttpRequest HttpRequest; struct HttpRequest { int id; /* ID counter */ SOCKET s; /* Socket on which to receive data */ SOCKADDR_IN addr; /* Address from which data is coming */ const char *zOptions; /* --notfound and/or --localauth options */ }; /* ** Prefix for a temporary file. */ static char *zTempPrefix; | > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | ** HTTP request. */ typedef struct HttpRequest HttpRequest; struct HttpRequest { int id; /* ID counter */ SOCKET s; /* Socket on which to receive data */ SOCKADDR_IN addr; /* Address from which data is coming */ int flags; /* Flags passed to win32_http_server() */ const char *zOptions; /* --notfound and/or --localauth options */ }; /* ** Prefix for a temporary file. */ static char *zTempPrefix; |
︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | return atoi(&zHdr[17]); } } zHdr++; } return 0; } /* ** Process a single incoming HTTP request. */ static void win32_http_request(void *pAppData){ HttpRequest *p = (HttpRequest*)pAppData; FILE *in = 0, *out = 0; int amt, got; int wanted = 0; char *z; char zRequestFName[MAX_PATH]; char zReplyFName[MAX_PATH]; char zCmd[2000]; /* Command-line to process the request */ char zHdr[2000]; /* The HTTP request header */ sqlite3_snprintf(MAX_PATH, zRequestFName, "%s_in%d.txt", zTempPrefix, p->id); sqlite3_snprintf(MAX_PATH, zReplyFName, "%s_out%d.txt", zTempPrefix, p->id); amt = 0; while( amt<sizeof(zHdr) ){ got = recv(p->s, &zHdr[amt], sizeof(zHdr)-1-amt, 0); | > > > > > > > > > > > > > > | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | return atoi(&zHdr[17]); } } zHdr++; } return 0; } /* ** Issue a fatal error. */ static NORETURN void winhttp_fatal( const char *zOp, const char *zService, const char *zErr ){ fossil_fatal("unable to %s service '%s': %s", zOp, zService, zErr); } /* ** Process a single incoming HTTP request. */ static void win32_http_request(void *pAppData){ HttpRequest *p = (HttpRequest*)pAppData; FILE *in = 0, *out = 0; int amt, got; int wanted = 0; char *z; char zCmdFName[MAX_PATH]; char zRequestFName[MAX_PATH]; char zReplyFName[MAX_PATH]; char zCmd[2000]; /* Command-line to process the request */ char zHdr[2000]; /* The HTTP request header */ sqlite3_snprintf(MAX_PATH, zCmdFName, "%s_cmd%d.txt", zTempPrefix, p->id); sqlite3_snprintf(MAX_PATH, zRequestFName, "%s_in%d.txt", zTempPrefix, p->id); sqlite3_snprintf(MAX_PATH, zReplyFName, "%s_out%d.txt", zTempPrefix, p->id); amt = 0; while( amt<sizeof(zHdr) ){ got = recv(p->s, &zHdr[amt], sizeof(zHdr)-1-amt, 0); |
︙ | ︙ | |||
106 107 108 109 110 111 112 | }else{ break; } wanted -= got; } fclose(out); out = 0; | > > > > > > > | > | > > > | > > > > > > > > > > | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | }else{ break; } wanted -= got; } fclose(out); out = 0; /* ** The repository name is only needed if there was no open checkout. This ** is designed to allow the open checkout for the interactive user to work ** with the local Fossil server started via the "ui" command. */ if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){ assert( g.zRepositoryName && g.zRepositoryName[0] ); sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s\n%s", get_utf8_bom(0), zRequestFName, zReplyFName, inet_ntoa(p->addr.sin_addr), g.zRepositoryName ); }else{ sqlite3_snprintf(sizeof(zCmd), zCmd, "%s%s\n%s\n%s", get_utf8_bom(0), zRequestFName, zReplyFName, inet_ntoa(p->addr.sin_addr) ); } out = fossil_fopen(zCmdFName, "wb"); if( out==0 ) goto end_request; fwrite(zCmd, 1, strlen(zCmd), out); fclose(out); sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http -args \"%s\" --nossl%s", g.nameOfExe, zCmdFName, p->zOptions ); fossil_system(zCmd); in = fossil_fopen(zReplyFName, "rb"); if( in ){ while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ send(p->s, zHdr, got, 0); } } end_request: if( out ) fclose(out); if( in ) fclose(in); closesocket(p->s); file_delete(zRequestFName); file_delete(zReplyFName); file_delete(zCmdFName); free(p); } /* ** Process a single incoming SCGI request. */ static void win32_scgi_request(void *pAppData){ |
︙ | ︙ | |||
166 167 168 169 170 171 172 173 | got = recv(p->s, zHdr, wanted<sizeof(zHdr) ? wanted : sizeof(zHdr), 0); if( got<=0 ) break; fwrite(zHdr, 1, got, out); wanted += got; } fclose(out); out = 0; sqlite3_snprintf(sizeof(zCmd), zCmd, | > | | | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | got = recv(p->s, zHdr, wanted<sizeof(zHdr) ? wanted : sizeof(zHdr), 0); if( got<=0 ) break; fwrite(zHdr, 1, got, out); wanted += got; } fclose(out); out = 0; assert( g.zRepositoryName && g.zRepositoryName[0] ); sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http \"%s\" \"%s\" %s \"%s\" --scgi --nossl%s", g.nameOfExe, zRequestFName, zReplyFName, inet_ntoa(p->addr.sin_addr), g.zRepositoryName, p->zOptions ); fossil_system(zCmd); in = fossil_fopen(zReplyFName, "rb"); if( in ){ while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ send(p->s, zHdr, got, 0); } |
︙ | ︙ | |||
221 222 223 224 225 226 227 228 229 230 231 232 233 234 | } if( zFileGlob ){ blob_appendf(&options, " --files-urlenc %T", zFileGlob); } if( g.useLocalauth ){ blob_appendf(&options, " --localauth"); } if( WSAStartup(MAKEWORD(1,1), &wd) ){ fossil_fatal("unable to initialize winsock"); } while( iPort<=mxPort ){ s = socket(AF_INET, SOCK_STREAM, 0); if( s==INVALID_SOCKET ){ fossil_fatal("unable to create a socket"); | > > > | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | } if( zFileGlob ){ blob_appendf(&options, " --files-urlenc %T", zFileGlob); } if( g.useLocalauth ){ blob_appendf(&options, " --localauth"); } if( flags & HTTP_SERVER_REPOLIST ){ blob_appendf(&options, " --repolist"); } if( WSAStartup(MAKEWORD(1,1), &wd) ){ fossil_fatal("unable to initialize winsock"); } while( iPort<=mxPort ){ s = socket(AF_INET, SOCK_STREAM, 0); if( s==INVALID_SOCKET ){ fossil_fatal("unable to create a socket"); |
︙ | ︙ | |||
269 270 271 272 273 274 275 | fossil_fatal("unable to get path to the temporary directory."); } zTempPrefix = mprintf("%sfossil_server_P%d_", fossil_unicode_to_utf8(zTmpPath), iPort); fossil_print("Listening for %s requests on TCP port %d\n", (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); if( zBrowser ){ | | | 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | fossil_fatal("unable to get path to the temporary directory."); } zTempPrefix = mprintf("%sfossil_server_P%d_", fossil_unicode_to_utf8(zTmpPath), iPort); fossil_print("Listening for %s requests on TCP port %d\n", (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); if( zBrowser ){ zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort); fossil_print("Launch webbrowser: %s\n", zBrowser); fossil_system(zBrowser); } fossil_print("Type Ctrl-C to stop the HTTP server\n"); /* Set the service status to running and pass the listener socket to the ** service handling procedures. */ win32_http_service_running(s); |
︙ | ︙ | |||
304 305 306 307 308 309 310 311 312 313 314 315 316 317 | }else if( zStopper && file_size(zStopper)>=0 ){ break; } p = fossil_malloc( sizeof(*p) ); p->id = ++idCnt; p->s = client; p->addr = client_addr; p->zOptions = blob_str(&options); if( flags & HTTP_SERVER_SCGI ){ _beginthread(win32_scgi_request, 0, (void*)p); }else{ _beginthread(win32_http_request, 0, (void*)p); } } | > | 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 | }else if( zStopper && file_size(zStopper)>=0 ){ break; } p = fossil_malloc( sizeof(*p) ); p->id = ++idCnt; p->s = client; p->addr = client_addr; p->flags = flags; p->zOptions = blob_str(&options); if( flags & HTTP_SERVER_SCGI ){ _beginthread(win32_scgi_request, 0, (void*)p); }else{ _beginthread(win32_http_request, 0, (void*)p); } } |
︙ | ︙ | |||
604 605 606 607 608 609 610 611 612 613 614 615 616 617 | ** ** --localauth ** ** Enables automatic login if the --localauth option is present ** and the "localauth" setting is off and the connection is from ** localhost. ** ** --scgi ** ** Create an SCGI server instead of an HTTP server ** ** ** fossil winsrv delete ?SERVICE-NAME? ** | > > > > | 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | ** ** --localauth ** ** Enables automatic login if the --localauth option is present ** and the "localauth" setting is off and the connection is from ** localhost. ** ** --repolist ** ** If REPOSITORY is directory, URL "/" lists all repositories. ** ** --scgi ** ** Create an SCGI server instead of an HTTP server ** ** ** fossil winsrv delete ?SERVICE-NAME? ** |
︙ | ︙ | |||
650 651 652 653 654 655 656 | n = strlen(zMethod); if( strncmp(zMethod, "create", n)==0 ){ SC_HANDLE hScm; SC_HANDLE hSvc; SERVICE_DESCRIPTIONW svcDescr = {L"Fossil - Distributed Software Configuration Management"}; | < | > | > > > > > | | > | | | < | | | | | < | 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 | n = strlen(zMethod); if( strncmp(zMethod, "create", n)==0 ){ SC_HANDLE hScm; SC_HANDLE hSvc; SERVICE_DESCRIPTIONW svcDescr = {L"Fossil - Distributed Software Configuration Management"}; DWORD dwStartType = SERVICE_DEMAND_START; const char *zDisplay = find_option("display", "D", 1); const char *zStart = find_option("start", "S", 1); const char *zUsername = find_option("username", "U", 1); const char *zPassword = find_option("password", "W", 1); const char *zPort = find_option("port", "P", 1); const char *zNotFound = find_option("notfound", 0, 1); const char *zFileGlob = find_option("files", 0, 1); const char *zLocalAuth = find_option("localauth", 0, 0); const char *zRepository = find_repository_option(); int useSCGI = find_option("scgi", 0, 0)!=0; int allowRepoList = find_option("repolist",0,0)!=0; Blob binPath; verify_all_options(); if( g.argc==4 ){ zSvcName = g.argv[3]; }else if( g.argc>4 ){ fossil_fatal("too many arguments for create method."); } /* Process service creation specific options. */ if( !zDisplay ){ zDisplay = zSvcName; } /* Per MSDN, the password parameter cannot be NULL. Must use empty ** string instead (i.e. in the call to CreateServiceW). */ if( !zPassword ){ zPassword = ""; } if( zStart ){ if( strncmp(zStart, "auto", strlen(zStart))==0 ){ dwStartType = SERVICE_AUTO_START; }else if( strncmp(zStart, "manual", strlen(zStart))==0 ){ dwStartType = SERVICE_DEMAND_START; }else{ winhttp_fatal("create", zSvcName, "specify 'auto' or 'manual' for the '-S|--start' option"); } } /* Process options for Fossil running as server. */ if( zPort && (atoi(zPort)<=0) ){ winhttp_fatal("create", zSvcName, "port number must be in the range 1 - 65535."); } if( !zRepository ){ db_must_be_within_tree(); }else if( file_isdir(zRepository)==1 ){ g.zRepositoryName = mprintf("%s", zRepository); file_simplify_name(g.zRepositoryName, -1, 0); }else{ db_open_repository(zRepository); } db_close(0); /* Build the fully-qualified path to the service binary file. */ blob_zero(&binPath); blob_appendf(&binPath, "\"%s\" server", g.nameOfExe); if( zPort ) blob_appendf(&binPath, " --port %s", zPort); if( useSCGI ) blob_appendf(&binPath, " --scgi"); if( allowRepoList ) blob_appendf(&binPath, " --repolist"); if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound); if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob); if( zLocalAuth ) blob_append(&binPath, " --localauth", -1); blob_appendf(&binPath, " \"%s\"", g.zRepositoryName); /* Create the service. */ hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if( !hScm ) winhttp_fatal("create", zSvcName, win32_get_last_errmsg()); hSvc = CreateServiceW( hScm, /* Handle to the SCM */ fossil_utf8_to_unicode(zSvcName), /* Name of the service */ fossil_utf8_to_unicode(zDisplay), /* Display name */ SERVICE_ALL_ACCESS, /* Desired access */ SERVICE_WIN32_OWN_PROCESS, /* Service type */ dwStartType, /* Start type */ SERVICE_ERROR_NORMAL, /* Error control */ fossil_utf8_to_unicode(blob_str(&binPath)), /* Binary path */ NULL, /* Load ordering group */ NULL, /* Tag value */ NULL, /* Service dependencies */ zUsername ? fossil_utf8_to_unicode(zUsername) : 0, /* Account */ fossil_utf8_to_unicode(zPassword) /* Account password */ ); if( !hSvc ) winhttp_fatal("create", zSvcName, win32_get_last_errmsg()); /* Set the service description. */ ChangeServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION, &svcDescr); fossil_print("Service '%s' successfully created.\n", zSvcName); CloseServiceHandle(hSvc); CloseServiceHandle(hScm); }else if( strncmp(zMethod, "delete", n)==0 ){ SC_HANDLE hScm; SC_HANDLE hSvc; SERVICE_STATUS sstat; verify_all_options(); if( g.argc==4 ){ zSvcName = g.argv[3]; }else if( g.argc>4 ){ fossil_fatal("too many arguments for delete method."); } hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if( !hScm ) winhttp_fatal("delete", zSvcName, win32_get_last_errmsg()); hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName), SERVICE_ALL_ACCESS); if( !hSvc ) winhttp_fatal("delete", zSvcName, win32_get_last_errmsg()); QueryServiceStatus(hSvc, &sstat); if( sstat.dwCurrentState!=SERVICE_STOPPED ){ fossil_print("Stopping service '%s'", zSvcName); if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){ if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){ winhttp_fatal("delete", zSvcName, win32_get_last_errmsg()); } } while( sstat.dwCurrentState!=SERVICE_STOPPED ){ Sleep(100); fossil_print("."); QueryServiceStatus(hSvc, &sstat); } fossil_print("\nService '%s' stopped.\n", zSvcName); } if( !DeleteService(hSvc) ){ if( GetLastError()==ERROR_SERVICE_MARKED_FOR_DELETE ){ fossil_warning("Service '%s' already marked for delete.\n", zSvcName); }else{ winhttp_fatal("delete", zSvcName, win32_get_last_errmsg()); } }else{ fossil_print("Service '%s' successfully deleted.\n", zSvcName); } CloseServiceHandle(hSvc); CloseServiceHandle(hScm); }else if( strncmp(zMethod, "show", n)==0 ){ SC_HANDLE hScm; SC_HANDLE hSvc; SERVICE_STATUS sstat; LPQUERY_SERVICE_CONFIGW pSvcConfig; LPSERVICE_DESCRIPTIONW pSvcDescr; BOOL bStatus; DWORD nRequired; static const char *const zSvcTypes[] = { "Driver service", "File system driver service", "Service runs in its own process", "Service shares a process with other services", "Service can interact with the desktop" }; |
︙ | ︙ | |||
811 812 813 814 815 816 817 | }; const char *zSvcState = ""; verify_all_options(); if( g.argc==4 ){ zSvcName = g.argv[3]; }else if( g.argc>4 ){ | | | | | | | | | | 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 | }; const char *zSvcState = ""; verify_all_options(); if( g.argc==4 ){ zSvcName = g.argv[3]; }else if( g.argc>4 ){ fossil_fatal("too many arguments for show method."); } hScm = OpenSCManagerW(NULL, NULL, GENERIC_READ); if( !hScm ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg()); hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName), GENERIC_READ); if( !hSvc ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg()); /* Get the service configuration */ bStatus = QueryServiceConfigW(hSvc, NULL, 0, &nRequired); if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){ winhttp_fatal("show", zSvcName, win32_get_last_errmsg()); } pSvcConfig = fossil_malloc(nRequired); bStatus = QueryServiceConfigW(hSvc, pSvcConfig, nRequired, &nRequired); if( !bStatus ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg()); /* Translate the service type */ switch( pSvcConfig->dwServiceType ){ case SERVICE_KERNEL_DRIVER: zSvcType = zSvcTypes[0]; break; case SERVICE_FILE_SYSTEM_DRIVER: zSvcType = zSvcTypes[1]; break; case SERVICE_WIN32_OWN_PROCESS: zSvcType = zSvcTypes[2]; break; case SERVICE_WIN32_SHARE_PROCESS: zSvcType = zSvcTypes[3]; break; case SERVICE_INTERACTIVE_PROCESS: zSvcType = zSvcTypes[4]; break; } /* Translate the service start type */ switch( pSvcConfig->dwStartType ){ case SERVICE_BOOT_START: zSvcStartType = zSvcStartTypes[0]; break; case SERVICE_SYSTEM_START: zSvcStartType = zSvcStartTypes[1]; break; case SERVICE_AUTO_START: zSvcStartType = zSvcStartTypes[2]; break; case SERVICE_DEMAND_START: zSvcStartType = zSvcStartTypes[3]; break; case SERVICE_DISABLED: zSvcStartType = zSvcStartTypes[4]; break; } /* Get the service description. */ bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION, NULL, 0, &nRequired); if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){ winhttp_fatal("show", zSvcName, win32_get_last_errmsg()); } pSvcDescr = fossil_malloc(nRequired); bStatus = QueryServiceConfig2W(hSvc, SERVICE_CONFIG_DESCRIPTION, (LPBYTE)pSvcDescr, nRequired, &nRequired); if( !bStatus ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg()); /* Retrieves the current status of the specified service. */ bStatus = QueryServiceStatus(hSvc, &sstat); if( !bStatus ) winhttp_fatal("show", zSvcName, win32_get_last_errmsg()); /* Translate the current state. */ switch( sstat.dwCurrentState ){ case SERVICE_STOPPED: zSvcState = zSvcStates[0]; break; case SERVICE_START_PENDING: zSvcState = zSvcStates[1]; break; case SERVICE_STOP_PENDING: zSvcState = zSvcStates[2]; break; case SERVICE_RUNNING: zSvcState = zSvcStates[3]; break; case SERVICE_CONTINUE_PENDING: zSvcState = zSvcStates[4]; break; |
︙ | ︙ | |||
887 888 889 890 891 892 893 | CloseServiceHandle(hSvc); CloseServiceHandle(hScm); }else if( strncmp(zMethod, "start", n)==0 ){ SC_HANDLE hScm; SC_HANDLE hSvc; SERVICE_STATUS sstat; | < | | | | < | | | | | 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 | CloseServiceHandle(hSvc); CloseServiceHandle(hScm); }else if( strncmp(zMethod, "start", n)==0 ){ SC_HANDLE hScm; SC_HANDLE hSvc; SERVICE_STATUS sstat; verify_all_options(); if( g.argc==4 ){ zSvcName = g.argv[3]; }else if( g.argc>4 ){ fossil_fatal("too many arguments for start method."); } hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg()); hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName), SERVICE_ALL_ACCESS); if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg()); QueryServiceStatus(hSvc, &sstat); if( sstat.dwCurrentState!=SERVICE_RUNNING ){ fossil_print("Starting service '%s'", zSvcName); if( sstat.dwCurrentState!=SERVICE_START_PENDING ){ if( !StartServiceW(hSvc, 0, NULL) ){ winhttp_fatal("start", zSvcName, win32_get_last_errmsg()); } } while( sstat.dwCurrentState!=SERVICE_RUNNING ){ Sleep(100); fossil_print("."); QueryServiceStatus(hSvc, &sstat); } fossil_print("\nService '%s' started.\n", zSvcName); }else{ fossil_print("Service '%s' is already started.\n", zSvcName); } CloseServiceHandle(hSvc); CloseServiceHandle(hScm); }else if( strncmp(zMethod, "stop", n)==0 ){ SC_HANDLE hScm; SC_HANDLE hSvc; SERVICE_STATUS sstat; verify_all_options(); if( g.argc==4 ){ zSvcName = g.argv[3]; }else if( g.argc>4 ){ fossil_fatal("too many arguments for stop method."); } hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if( !hScm ) winhttp_fatal("stop", zSvcName, win32_get_last_errmsg()); hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName), SERVICE_ALL_ACCESS); if( !hSvc ) winhttp_fatal("stop", zSvcName, win32_get_last_errmsg()); QueryServiceStatus(hSvc, &sstat); if( sstat.dwCurrentState!=SERVICE_STOPPED ){ fossil_print("Stopping service '%s'", zSvcName); if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){ if( !ControlService(hSvc, SERVICE_CONTROL_STOP, &sstat) ){ winhttp_fatal("stop", zSvcName, win32_get_last_errmsg()); } } while( sstat.dwCurrentState!=SERVICE_STOPPED ){ Sleep(100); fossil_print("."); QueryServiceStatus(hSvc, &sstat); } |
︙ | ︙ |
Changes to src/wysiwyg.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code that generates WYSIWYG text editors on ** web pages. */ #include <assert.h> #include <ctype.h> | > < | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code that generates WYSIWYG text editors on ** web pages. */ #include "config.h" #include <assert.h> #include <ctype.h> #include "wysiwyg.h" /* ** Output code for a WYSIWYG editor. The caller must have already generated ** the <form> that will contain the editor, and the call must generate the ** corresponding </form> after this routine returns. The caller must include |
︙ | ︙ | |||
226 227 228 229 230 231 232 | @ </div> @ <div id="wysiwygBox" @ style="resize:both; overflow:auto; width: %d(w)em; height: %d(h)em;" @ contenteditable="true">%s(zContent)</div> @ <script> @ var oDoc; | | | | | | > > > > | > > > > > > > > | | | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | @ </div> @ <div id="wysiwygBox" @ style="resize:both; overflow:auto; width: %d(w)em; height: %d(h)em;" @ contenteditable="true">%s(zContent)</div> @ <script> @ var oDoc; @ @ /* Initialize the document editor */ @ function initDoc() { @ oDoc = document.getElementById("wysiwygBox"); @ if (!isWysiwyg()) { setDocMode(true); } @ } @ @ /* Return true if the document editor is in WYSIWYG mode. Return @ ** false if it is in Markup mode */ @ function isWysiwyg() { @ return document.getElementById("editMode").selectedIndex==0; @ } @ @ /* Invoke this routine prior to submitting the HTML content back @ ** to the server */ @ function wysiwygSubmit() { @ if(oDoc.style.whiteSpace=="pre-wrap"){setDocMode(0);} @ document.getElementById("wysiwygValue").value=oDoc.innerHTML; @ } @ @ /* Run the editing command if in WYSIWYG mode */ @ function formatDoc(sCmd, sValue) { @ if (isWysiwyg()){ @ try { @ // First, try the W3C draft standard way, which has @ // been working on all non-IE browsers for a while. @ // It is also supported by IE11 and higher. @ document.execCommand("styleWithCSS", false, false); @ } catch (e) { @ try { @ // For IE9 or IE10, this should work. @ document.execCommand("useCSS", 0, true); @ } catch (e) { @ // Ok, that apparently did not work, do nothing. @ } @ } @ document.execCommand(sCmd, false, sValue); @ oDoc.focus(); @ } @ } @ @ /* Change the editing mode. Convert to markup if the argument @ ** is true and wysiwyg if the argument is false. */ @ function setDocMode(bToMarkup) { @ var oContent; @ if (bToMarkup) { @ /* WYSIWYG -> Markup */ @ var linebreak = new RegExp("</p><p>","ig"); @ oContent = document.createTextNode( @ oDoc.innerHTML.replace(linebreak,"</p>\n\n<p>")); |
︙ | ︙ |
Changes to src/xfer.c.
︙ | ︙ | |||
92 93 94 95 96 97 98 | db_bind_int(&q, ":r", rid); db_step(&q); db_reset(&q); } } /* | | | > > > > > | | | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | db_bind_int(&q, ":r", rid); db_step(&q); db_reset(&q); } } /* ** The aToken[0..nToken-1] blob array is a parse of a "file" line ** message. This routine finishes parsing that message and does ** a record insert of the file. ** ** The file line is in one of the following two forms: ** ** file UUID SIZE \n CONTENT ** file UUID DELTASRC SIZE \n CONTENT ** ** The content is SIZE bytes immediately following the newline. ** If DELTASRC exists, then the CONTENT is a delta against the ** content of DELTASRC. ** ** If any error occurs, write a message into pErr which has already ** be initialized to an empty string. ** ** Any artifact successfully received by this routine is considered to ** be public and is therefore removed from the "private" table. */ static void xfer_accept_file( Xfer *pXfer, int cloneFlag, char **pzUuidList, int *pnUuidList ){ int n; int rid; int srcid = 0; Blob content, hash; int isPriv; isPriv = pXfer->nextIsPrivate; pXfer->nextIsPrivate = 0; if( pXfer->nToken<3 || pXfer->nToken>4 || !blob_is_uuid(&pXfer->aToken[1]) || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n) || n<0 || (pXfer->nToken==4 && !blob_is_uuid(&pXfer->aToken[2])) ){ blob_appendf(&pXfer->err, "malformed file line"); |
︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | pXfer->nDeltaRcvd++; }else{ srcid = 0; pXfer->nFileRcvd++; } rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, 0, isPriv); remote_has(rid); blob_reset(&content); return; } if( pXfer->nToken==4 ){ Blob src, next; srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv); if( content_get(srcid, &src)==0 ){ rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, 0, isPriv); pXfer->nDanglingFile++; db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); if( !isPriv ) content_make_public(rid); blob_reset(&src); blob_reset(&content); return; } pXfer->nDeltaRcvd++; blob_delta_apply(&src, &content, &next); blob_reset(&src); blob_reset(&content); content = next; }else{ pXfer->nFileRcvd++; } sha1sum_blob(&content, &hash); if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ blob_appendf(&pXfer->err, "content does not match sha1 hash"); } rid = content_put_ex(&content, blob_str(&hash), 0, 0, isPriv); blob_reset(&hash); if( rid==0 ){ blob_appendf(&pXfer->err, "%s", g.zErrMsg); blob_reset(&content); }else{ if( !isPriv ) content_make_public(rid); | > > > > > | | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | pXfer->nDeltaRcvd++; }else{ srcid = 0; pXfer->nFileRcvd++; } rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, 0, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(&pXfer->aToken[1]), blob_size(&pXfer->aToken[1])); remote_has(rid); blob_reset(&content); return; } if( pXfer->nToken==4 ){ Blob src, next; srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv); if( content_get(srcid, &src)==0 ){ rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, 0, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(&pXfer->aToken[1]), blob_size(&pXfer->aToken[1])); pXfer->nDanglingFile++; db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); if( !isPriv ) content_make_public(rid); blob_reset(&src); blob_reset(&content); return; } pXfer->nDeltaRcvd++; blob_delta_apply(&src, &content, &next); blob_reset(&src); blob_reset(&content); content = next; }else{ pXfer->nFileRcvd++; } sha1sum_blob(&content, &hash); if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ blob_appendf(&pXfer->err, "content does not match sha1 hash"); } rid = content_put_ex(&content, blob_str(&hash), 0, 0, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(&hash), blob_size(&hash)); blob_reset(&hash); if( rid==0 ){ blob_appendf(&pXfer->err, "%s", g.zErrMsg); blob_reset(&content); }else{ if( !isPriv ) content_make_public(rid); manifest_crosslink(rid, &content, MC_NO_ERRORS); } assert( blob_is_reset(&content) ); remote_has(rid); } /* ** The aToken[0..nToken-1] blob array is a parse of a "cfile" line ** message. This routine finishes parsing that message and does ** a record insert of the file. The difference between "file" and ** "cfile" is that with "cfile" the content is already compressed. ** ** The file line is in one of the following two forms: ** ** cfile UUID USIZE CSIZE \n CONTENT |
︙ | ︙ | |||
218 219 220 221 222 223 224 | ** ** If any error occurs, write a message into pErr which has already ** be initialized to an empty string. ** ** Any artifact successfully received by this routine is considered to ** be public and is therefore removed from the "private" table. */ | | > > > > | | | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | ** ** If any error occurs, write a message into pErr which has already ** be initialized to an empty string. ** ** Any artifact successfully received by this routine is considered to ** be public and is therefore removed from the "private" table. */ static void xfer_accept_compressed_file( Xfer *pXfer, char **pzUuidList, int *pnUuidList ){ int szC; /* CSIZE */ int szU; /* USIZE */ int rid; int srcid = 0; Blob content; int isPriv; isPriv = pXfer->nextIsPrivate; pXfer->nextIsPrivate = 0; if( pXfer->nToken<4 || pXfer->nToken>5 || !blob_is_uuid(&pXfer->aToken[1]) || !blob_is_int(&pXfer->aToken[pXfer->nToken-2], &szU) || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &szC) || szC<0 || szU<0 || (pXfer->nToken==5 && !blob_is_uuid(&pXfer->aToken[2])) ){ |
︙ | ︙ | |||
259 260 261 262 263 264 265 266 267 268 269 270 271 272 | pXfer->nDeltaRcvd++; }else{ srcid = 0; pXfer->nFileRcvd++; } rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, szC, isPriv); remote_has(rid); blob_reset(&content); } /* ** Try to send a file as a delta against its parent. ** If successful, return the number of bytes in the delta. | > > | 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | pXfer->nDeltaRcvd++; }else{ srcid = 0; pXfer->nFileRcvd++; } rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, szC, isPriv); Th_AppendToList(pzUuidList, pnUuidList, blob_str(&pXfer->aToken[1]), blob_size(&pXfer->aToken[1])); remote_has(rid); blob_reset(&content); } /* ** Try to send a file as a delta against its parent. ** If successful, return the number of bytes in the delta. |
︙ | ︙ | |||
282 283 284 285 286 287 288 | Blob *pContent, /* The content of the file to send */ Blob *pUuid /* The UUID of the file to send */ ){ static const char *const azQuery[] = { "SELECT pid FROM plink x" " WHERE cid=%d" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", | | | | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | Blob *pContent, /* The content of the file to send */ Blob *pUuid /* The UUID of the file to send */ ){ static const char *const azQuery[] = { "SELECT pid FROM plink x" " WHERE cid=%d" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)", "SELECT pid, min(mtime) FROM mlink, event ON mlink.mid=event.objid" " WHERE fid=%d" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" }; int i; Blob src, delta; int size = 0; int srcId = 0; for(i=0; srcId==0 && i<count(azQuery); i++){ srcId = db_int(0, azQuery[i] /*works-like:"%d"*/, rid); } if( srcId>0 && (pXfer->syncPrivate || !content_is_private(srcId)) && content_get(srcId, &src) ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId); blob_delta_create(&src, pContent, &delta); |
︙ | ︙ | |||
319 320 321 322 323 324 325 | free(zUuid); blob_reset(&src); } return size; } /* | | | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | free(zUuid); blob_reset(&src); } return size; } /* ** Try to send a file as a native delta. ** If successful, return the number of bytes in the delta. ** If we cannot generate an appropriate delta, then send ** nothing and return zero. ** ** Never send a delta against a private artifact. */ static int send_delta_native( |
︙ | ︙ | |||
401 402 403 404 405 406 407 | }else{ pUuid = &uuid; } if( uuid_is_shunned(blob_str(pUuid)) ){ blob_reset(&uuid); return; } | | | | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | }else{ pUuid = &uuid; } if( uuid_is_shunned(blob_str(pUuid)) ){ blob_reset(&uuid); return; } if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) || pXfer->mxSend<=blob_size(pXfer->pOut) ){ const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n"; blob_appendf(pXfer->pOut, zFormat /*works-like:"%b"*/, pUuid); pXfer->nIGotSent++; blob_reset(&uuid); return; } if( nativeDelta ){ size = send_delta_native(pXfer, rid, isPriv, pUuid); if( size ){ |
︙ | ︙ | |||
436 437 438 439 440 441 442 | } blob_reset(&content); } remote_has(rid); blob_reset(&uuid); #if 0 if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){ | | | | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | } blob_reset(&content); } remote_has(rid); blob_reset(&uuid); #if 0 if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){ blob_append(pXfer->pOut, "\n", 1); } #endif } /* ** Send the file identified by rid as a compressed artifact. Basically, ** send the content exactly as it appears in the BLOB table using ** a "cfile" card. */ static void send_compressed_file(Xfer *pXfer, int rid){ const char *zContent; const char *zUuid; const char *zDelta; int szU; |
︙ | ︙ | |||
496 497 498 499 500 501 502 | pXfer->nDeltaSent++; }else{ pXfer->nFileSent++; } blob_appendf(pXfer->pOut, "%d %d\n", szU, szC); blob_append(pXfer->pOut, zContent, szC); if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){ | | | | | 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 | pXfer->nDeltaSent++; }else{ pXfer->nFileSent++; } blob_appendf(pXfer->pOut, "%d %d\n", szU, szC); blob_append(pXfer->pOut, zContent, szC); if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){ blob_append(pXfer->pOut, "\n", 1); } if( !isPrivate && srcIsPrivate ){ blob_reset(&fullContent); } } db_reset(&q1); } /* ** Send a gimme message for every phantom. ** ** Except: do not request shunned artifacts. And do not request ** private artifacts if we are not doing a private transfer. */ static void request_phantoms(Xfer *pXfer, int maxReq){ Stmt q; db_prepare(&q, "SELECT uuid FROM phantom CROSS JOIN blob USING(rid) /*scan*/" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid) %s", (pXfer->syncPrivate ? "" : " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)") ); while( db_step(&q)==SQLITE_ROW && maxReq-- > 0 ){ const char *zUuid = db_column_text(&q, 0); blob_appendf(pXfer->pOut, "gimme %s\n", zUuid); |
︙ | ︙ | |||
549 550 551 552 553 554 555 | /* ** Check the signature on an application/x-fossil payload received by ** the HTTP server. The signature is a line of the following form: ** ** login LOGIN NONCE SIGNATURE ** | | | | | > | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 | /* ** Check the signature on an application/x-fossil payload received by ** the HTTP server. The signature is a line of the following form: ** ** login LOGIN NONCE SIGNATURE ** ** The NONCE is the SHA1 hash of the remainder of the input. ** SIGNATURE is the SHA1 checksum of the NONCE concatenated ** with the users password. ** ** The parameters to this routine are ephemeral blobs holding the ** LOGIN, NONCE and SIGNATURE. ** ** This routine attempts to locate the user and verify the signature. ** If everything checks out, the USER.CAP column for the USER table ** is consulted to set privileges in the global g variable. ** ** If anything fails to check out, no changes are made to privileges. ** ** Signature generation on the client side is handled by the ** http_exchange() routine. ** ** Return non-zero for a login failure and zero for success. */ int check_login(Blob *pLogin, Blob *pNonce, Blob *pSig){ Stmt q; int rc = -1; char *zLogin = blob_terminate(pLogin); defossilize(zLogin); if( fossil_strcmp(zLogin, "nobody")==0 || fossil_strcmp(zLogin,"anonymous")==0 ){ return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ } if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0 && db_get_boolean("remote_user_ok",0) ){ return 0; /* Accept Basic Authorization */ } db_prepare(&q, "SELECT pw, cap, uid FROM user" " WHERE login=%Q" " AND login NOT IN ('anonymous','nobody','developer','reader')" " AND length(pw)>0", |
︙ | ︙ | |||
692 693 694 695 696 697 698 | md5sum_blob(&cluster, &cksum); blob_appendf(&cluster, "Z %b\n", &cksum); blob_reset(&cksum); rid = content_put(&cluster); blob_reset(&cluster); nUncl -= nRow; nRow = 0; | | | > | | 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 | md5sum_blob(&cluster, &cksum); blob_appendf(&cluster, "Z %b\n", &cksum); blob_reset(&cksum); rid = content_put(&cluster); blob_reset(&cluster); nUncl -= nRow; nRow = 0; blob_append_sql(&deleteWhere, ",%d", rid); } } db_finalize(&q); db_multi_exec( "DELETE FROM unclustered WHERE rid NOT IN (0 %s)" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=unclustered.rid)", blob_sql_text(&deleteWhere) ); blob_reset(&deleteWhere); if( nRow>0 ){ md5sum_blob(&cluster, &cksum); blob_appendf(&cluster, "Z %b\n", &cksum); blob_reset(&cksum); content_put(&cluster); |
︙ | ︙ | |||
736 737 738 739 740 741 742 | ** Send an igot message for every entry in unclustered table. ** Return the number of cards sent. */ static int send_unclustered(Xfer *pXfer){ Stmt q; int cnt = 0; if( pXfer->resync ){ | | | | 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 | ** Send an igot message for every entry in unclustered table. ** Return the number of cards sent. */ static int send_unclustered(Xfer *pXfer){ Stmt q; int cnt = 0; if( pXfer->resync ){ db_prepare(&q, "SELECT uuid, rid FROM blob" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" " AND blob.rid<=%d" " ORDER BY blob.rid DESC", pXfer->resync ); }else{ db_prepare(&q, "SELECT uuid FROM unclustered JOIN blob USING(rid)" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" ); } while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
770 771 772 773 774 775 776 | } /* ** Send an igot message for every artifact. */ static void send_all(Xfer *pXfer){ Stmt q; | | | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 | } /* ** Send an igot message for every artifact. */ static void send_all(Xfer *pXfer){ Stmt q; db_prepare(&q, "SELECT uuid FROM blob " " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" ); while( db_step(&q)==SQLITE_ROW ){ blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); |
︙ | ︙ | |||
818 819 820 821 822 823 824 | ** from a server without authorization. */ static void server_private_xfer_not_authorized(void){ @ error not\sauthorized\sto\ssync\sprivate\scontent } /* | > > > > | > | > > | | > | > > > > > | | > > > > | | | > > > > > > > > > > > > | | > > > > > > > > | | | 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 | ** from a server without authorization. */ static void server_private_xfer_not_authorized(void){ @ error not\sauthorized\sto\ssync\sprivate\scontent } /* ** Return the common TH1 code to evaluate prior to evaluating any other ** TH1 transfer notification scripts. */ const char *xfer_common_code(void){ return db_get("xfer-common-script", 0); } /* ** Return the TH1 code to evaluate when a push is processed. */ const char *xfer_push_code(void){ return db_get("xfer-push-script", 0); } /* ** Return the TH1 code to evaluate when a commit is processed. */ const char *xfer_commit_code(void){ return db_get("xfer-commit-script", 0); } /* ** Return the TH1 code to evaluate when a ticket change is processed. */ const char *xfer_ticket_code(void){ return db_get("xfer-ticket-script", 0); } /* ** Run the specified TH1 script, if any, and returns 1 on error. */ int xfer_run_script( const char *zScript, const char *zUuidOrList, int bIsList ){ int rc = TH_OK; if( !zScript ) return rc; Th_FossilInit(TH_INIT_DEFAULT); Th_Store(bIsList ? "uuids" : "uuid", zUuidOrList ? zUuidOrList : ""); rc = Th_Eval(g.interp, 0, zScript, -1); if( rc!=TH_OK ){ fossil_error(1, "%s", Th_GetResult(g.interp, 0)); } return rc; } /* ** Runs the pre-transfer TH1 script, if any, and returns its return code. ** This script may be run multiple times. If the script performs actions ** that cannot be redone, it should use an internal [if] guard similar to ** the following: ** ** if {![info exists common_done]} { ** # ... code here ** set common_done 1 ** } */ int xfer_run_common_script(void){ return xfer_run_script(xfer_common_code(), 0, 0); } /* ** If this variable is set, disable login checks. Used for debugging ** only. */ static int disableLogin = 0; |
︙ | ︙ | |||
873 874 875 876 877 878 879 880 881 882 883 884 885 886 | Xfer xfer; int deltaFlag = 0; int isClone = 0; int nGimme = 0; int size; int recvConfig = 0; char *zNow; if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ fossil_redirect_home(); } g.zLogin = "anonymous"; login_set_anon_nobody_capabilities(); login_check_credentials(); | > > > > > > | 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 | Xfer xfer; int deltaFlag = 0; int isClone = 0; int nGimme = 0; int size; int recvConfig = 0; char *zNow; int rc; const char *zScript = 0; char *zUuidList = 0; int nUuidList = 0; char **pzUuidList = 0; int *pnUuidList = 0; if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ fossil_redirect_home(); } g.zLogin = "anonymous"; login_set_anon_nobody_capabilities(); login_check_credentials(); |
︙ | ︙ | |||
902 903 904 905 906 907 908 | g.xferPanic = 1; db_begin_transaction(); db_multi_exec( "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" ); manifest_crosslink_begin(); | | > | > > > > > | | | 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 | g.xferPanic = 1; db_begin_transaction(); db_multi_exec( "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" ); manifest_crosslink_begin(); rc = xfer_run_common_script(); if( rc==TH_ERROR ){ cgi_reset_content(); @ error common\sscript\sfailed:\s%F(g.zErrMsg) nErr++; } zScript = xfer_push_code(); if( zScript ){ /* NOTE: Are TH1 transfer hooks enabled? */ pzUuidList = &zUuidList; pnUuidList = &nUuidList; } while( blob_line(xfer.pIn, &xfer.line) ){ if( blob_buffer(&xfer.line)[0]=='#' ) continue; if( blob_size(&xfer.line)==0 ) continue; xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); /* file UUID SIZE \n CONTENT ** file UUID DELTASRC SIZE \n CONTENT ** ** Accept a file from the client. */ if( blob_eq(&xfer.aToken[0], "file") ){ if( !isPush ){ cgi_reset_content(); @ error not\sauthorized\sto\swrite nErr++; break; } xfer_accept_file(&xfer, 0, pzUuidList, pnUuidList); if( blob_size(&xfer.err) ){ cgi_reset_content(); @ error %T(blob_str(&xfer.err)) nErr++; break; } }else /* cfile UUID USIZE CSIZE \n CONTENT ** cfile UUID DELTASRC USIZE CSIZE \n CONTENT ** ** Accept a file from the client. */ if( blob_eq(&xfer.aToken[0], "cfile") ){ if( !isPush ){ cgi_reset_content(); @ error not\sauthorized\sto\swrite nErr++; break; } xfer_accept_compressed_file(&xfer, pzUuidList, pnUuidList); if( blob_size(&xfer.err) ){ cgi_reset_content(); @ error %T(blob_str(&xfer.err)) nErr++; break; } }else |
︙ | ︙ | |||
990 991 992 993 994 995 996 | }else if( g.perm.Private ){ rid_from_uuid(&xfer.aToken[1], 1, 1); }else{ server_private_xfer_not_authorized(); } } }else | | | | 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 | }else if( g.perm.Private ){ rid_from_uuid(&xfer.aToken[1], 1, 1); }else{ server_private_xfer_not_authorized(); } } }else /* pull SERVERCODE PROJECTCODE ** push SERVERCODE PROJECTCODE ** ** The client wants either send or receive. The server should ** verify that the project code matches. */ if( xfer.nToken==3 |
︙ | ︙ | |||
1099 1100 1101 1102 1103 1104 1105 | if( check_tail_hash(&xfer.aToken[2], xfer.pIn) || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]) ){ cgi_reset_content(); @ error login\sfailed nErr++; break; | | | | 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 | if( check_tail_hash(&xfer.aToken[2], xfer.pIn) || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]) ){ cgi_reset_content(); @ error login\sfailed nErr++; break; } } }else /* reqconfig NAME ** ** Request a configuration value */ if( blob_eq(&xfer.aToken[0], "reqconfig") && xfer.nToken==2 ){ |
︙ | ︙ | |||
1124 1125 1126 1127 1128 1129 1130 | configure_send_group(xfer.pOut, groupMask, 0); }else if( configure_is_exportable(zName) ){ /* Old style configuration transfer */ send_legacy_config_card(&xfer, zName); } } }else | | | 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 | configure_send_group(xfer.pOut, groupMask, 0); }else if( configure_is_exportable(zName) ){ /* Old style configuration transfer */ send_legacy_config_card(&xfer, zName); } } }else /* config NAME SIZE \n CONTENT ** ** Receive a configuration value from the client. This is only ** permitted for high-privilege users. */ if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3 && blob_is_int(&xfer.aToken[2], &size) ){ |
︙ | ︙ | |||
1151 1152 1153 1154 1155 1156 1157 | recvConfig = 1; } configure_receive(zName, &content, CONFIGSET_ALL); blob_reset(&content); blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR); }else | | | 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 | recvConfig = 1; } configure_receive(zName, &content, CONFIGSET_ALL); blob_reset(&content); blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR); }else /* cookie TEXT ** ** A cookie contains a arbitrary-length argument that is server-defined. ** The argument must be encoded so as not to contain any whitespace. ** The server can optionally send a cookie to the client. The client ** might then return the same cookie back to the server on its next |
︙ | ︙ | |||
1226 1227 1228 1229 1230 1231 1232 1233 1234 | /* Unknown message */ { cgi_reset_content(); @ error bad\scommand:\s%F(blob_str(&xfer.line)) } blobarray_reset(xfer.aToken, xfer.nToken); } if( isPush ){ | > > > | | | | > > > > > | > | 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 | /* Unknown message */ { cgi_reset_content(); @ error bad\scommand:\s%F(blob_str(&xfer.line)) } blobarray_reset(xfer.aToken, xfer.nToken); blob_reset(&xfer.line); } if( isPush ){ if( rc==TH_OK ){ rc = xfer_run_script(zScript, zUuidList, 1); if( rc==TH_ERROR ){ cgi_reset_content(); @ error push\sscript\sfailed:\s%F(g.zErrMsg) nErr++; } } request_phantoms(&xfer, 500); } if( zUuidList ){ Th_Free(g.interp, zUuidList); } if( isClone && nGimme==0 ){ /* The initial "clone" message from client to server contains no ** "gimme" cards. On that initial message, send the client an "igot" ** card for every artifact currently in the repository. This will ** cause the client to create phantoms for all artifacts, which will ** in turn make sure that the entire repository is sent efficiently ** and expeditiously. */ send_all(&xfer); if( xfer.syncPrivate ) send_private(&xfer); }else if( isPull ){ create_cluster(); send_unclustered(&xfer); if( xfer.syncPrivate ) send_private(&xfer); } if( recvConfig ){ configure_finalize_receive(); } db_multi_exec("DROP TABLE onremote"); manifest_crosslink_end(MC_PERMIT_HOOKS); /* Send the server timestamp last, in case prior processing happened ** to use up a significant fraction of our time window. */ zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); @ # timestamp %s(zNow) free(zNow); db_end_transaction(0); configure_rebuild(); } /* ** COMMAND: test-xfer ** ** This command is used for debugging the server. There is a single ** argument which is the uncompressed content of an "xfer" message |
︙ | ︙ | |||
1325 1326 1327 1328 1329 1330 1331 | ** Floating-point absolute value */ static double fossil_fabs(double x){ return x>0.0 ? x : -x; } /* | | | 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 | ** Floating-point absolute value */ static double fossil_fabs(double x){ return x>0.0 ? x : -x; } /* ** Sync to the host identified in g.url.name and g.url.path. This ** routine is called by the client. ** ** Records are pushed to the server if pushFlag is true. Records ** are pulled if pullFlag is true. A full sync occurs if both are ** true. */ int client_sync( |
︙ | ︙ | |||
1364 1365 1366 1367 1368 1369 1370 | int nRoundtrip= 0; /* Number of HTTP requests */ int nArtifactSent = 0; /* Total artifacts sent */ int nArtifactRcvd = 0; /* Total artifacts received */ const char *zOpType = 0;/* Push, Pull, Sync, Clone */ double rSkew = 0.0; /* Maximum time skew */ if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; | | < < < < < | 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 | int nRoundtrip= 0; /* Number of HTTP requests */ int nArtifactSent = 0; /* Total artifacts sent */ int nArtifactRcvd = 0; /* Total artifacts received */ const char *zOpType = 0;/* Push, Pull, Sync, Clone */ double rSkew = 0.0; /* Maximum time skew */ if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE))==0 && configRcvMask==0 && configSendMask==0 ) return 0; transport_stats(0, 0, 1); socket_global_init(); memset(&xfer, 0, sizeof(xfer)); xfer.pIn = &recv; xfer.pOut = &send; xfer.mxSend = db_get_int("max-upload", 250000); xfer.maxTime = -1; if( syncFlags & SYNC_PRIVATE ){ g.perm.Private = 1; xfer.syncPrivate = 1; } blobarray_zero(xfer.aToken, count(xfer.aToken)); blob_zero(&send); blob_zero(&recv); blob_zero(&xfer.err); blob_zero(&xfer.line); origConfigRcvMask = 0; |
︙ | ︙ | |||
1422 1423 1424 1425 1426 1427 1428 | } if( syncFlags & SYNC_PUSH ){ blob_appendf(&send, "push %s %s\n", zSCode, zPCode); nCardSent++; if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push"; if( (syncFlags & SYNC_RESYNC)!=0 ) xfer.resync = 0x7fffffff; } | < < > | > > > > > > | | | 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 | } if( syncFlags & SYNC_PUSH ){ blob_appendf(&send, "push %s %s\n", zSCode, zPCode); nCardSent++; if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push"; if( (syncFlags & SYNC_RESYNC)!=0 ) xfer.resync = 0x7fffffff; } if( syncFlags & SYNC_VERBOSE ){ fossil_print(zLabelFormat /*works-like:"%s%s%s%s%d"*/, "", "Bytes", "Cards", "Artifacts", "Deltas"); } while( go ){ int newPhantom = 0; char *zRandomness; db_begin_transaction(); db_record_repository_filename(0); db_multi_exec( "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" ); manifest_crosslink_begin(); /* Send make the most recently received cookie. Let the server ** figure out if this is a cookie that it cares about. */ zCookie = db_get("cookie", 0); if( zCookie ){ blob_appendf(&send, "cookie %s\n", zCookie); } /* Generate gimme cards for phantoms and leaf cards ** for all leaves. */ if( (syncFlags & SYNC_PULL)!=0 || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1) ){ request_phantoms(&xfer, mxPhantomReq); } if( syncFlags & SYNC_PUSH ){ send_unsent(&xfer); nCardSent += send_unclustered(&xfer); if( syncFlags & SYNC_PRIVATE ) send_private(&xfer); } /* Send configuration parameter requests. On a clone, delay sending ** this until the second cycle since the login card might fail on ** the first cycle. */ if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){ const char *zName; if( zOpType==0 ) zOpType = "Pull"; zName = configure_first_name(configRcvMask); while( zName ){ |
︙ | ︙ | |||
1502 1503 1504 1505 1506 1507 1508 1509 1510 | ** messages unique so that that the login-card nonce will always ** be unique. */ zRandomness = db_text(0, "SELECT hex(randomblob(20))"); blob_appendf(&send, "# %s\n", zRandomness); free(zRandomness); /* Exchange messages with the server */ if( syncFlags & SYNC_VERBOSE ){ | > > > > > > > > > > > > | > | < < | < < < < < < | 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 | ** messages unique so that that the login-card nonce will always ** be unique. */ zRandomness = db_text(0, "SELECT hex(randomblob(20))"); blob_appendf(&send, "# %s\n", zRandomness); free(zRandomness); if( syncFlags & SYNC_VERBOSE ){ fossil_print("waiting for server..."); } fflush(stdout); /* Exchange messages with the server */ if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0, MAX_REDIRECTS) ){ nErr++; go = 2; break; } /* Output current stats */ if( syncFlags & SYNC_VERBOSE ){ fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:", blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, xfer.nFileSent, xfer.nDeltaSent); }else{ nRoundtrip++; nArtifactSent += xfer.nFileSent + xfer.nDeltaSent; fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, nRoundtrip, nArtifactSent, nArtifactRcvd); } nCardSent = 0; nCardRcvd = 0; xfer.nFileSent = 0; xfer.nDeltaSent = 0; xfer.nGimmeSent = 0; xfer.nIGotSent = 0; lastPctDone = -1; blob_reset(&send); rArrivalTime = db_double(0.0, "SELECT julianday('now')"); /* Send the send-private pragma if we are trying to sync private data */ if( syncFlags & SYNC_PRIVATE ){ blob_append(&send, "pragma send-private\n", -1); |
︙ | ︙ | |||
1583 1584 1585 1586 1587 1588 1589 | /* file UUID SIZE \n CONTENT ** file UUID DELTASRC SIZE \n CONTENT ** ** Receive a file transmitted from the server. */ if( blob_eq(&xfer.aToken[0],"file") ){ | | | | | | | < < < < | | 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 | /* file UUID SIZE \n CONTENT ** file UUID DELTASRC SIZE \n CONTENT ** ** Receive a file transmitted from the server. */ if( blob_eq(&xfer.aToken[0],"file") ){ xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0, 0, 0); nArtifactRcvd++; }else /* cfile UUID USIZE CSIZE \n CONTENT ** cfile UUID DELTASRC USIZE CSIZE \n CONTENT ** ** Receive a compressed file transmitted from the server. */ if( blob_eq(&xfer.aToken[0],"cfile") ){ xfer_accept_compressed_file(&xfer, 0, 0); nArtifactRcvd++; }else /* gimme UUID ** ** Server is requesting a file. If the file is a manifest, assume ** that the server will also want to know all of the content files ** associated with the manifest and send those too. */ if( blob_eq(&xfer.aToken[0], "gimme") && xfer.nToken==2 && blob_is_uuid(&xfer.aToken[1]) ){ if( syncFlags & SYNC_PUSH ){ int rid = rid_from_uuid(&xfer.aToken[1], 0, 0); if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0); } }else /* igot UUID ?PRIVATEFLAG? ** ** Server announces that it has a particular file. If this is ** not a file that we have and we are pulling, then create a ** phantom to cause this file to be requested on the next cycle. ** Always remember that the server has this file so that we do ** not transmit it by accident. ** ** If the PRIVATE argument exists and is 1, then the file is ** private. Pretend it does not exists if we are not pulling ** private files. */ if( xfer.nToken>=2 && blob_eq(&xfer.aToken[0], "igot") && blob_is_uuid(&xfer.aToken[1]) ){ int rid; int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1"); rid = rid_from_uuid(&xfer.aToken[1], 0, 0); if( rid>0 ){ if( !isPriv ) content_make_public(rid); }else if( isPriv && !g.perm.Private ){ /* ignore private files */ }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){ rid = content_new(blob_str(&xfer.aToken[1]), isPriv); if( rid ) newPhantom = 1; } remote_has(rid); }else /* push SERVERCODE PRODUCTCODE ** ** Should only happen in response to a clone. This message tells ** the client what product to use for the new database. */ if( blob_eq(&xfer.aToken[0],"push") && xfer.nToken==3 && (syncFlags & SYNC_CLONE)!=0 && blob_is_uuid(&xfer.aToken[2]) ){ if( zPCode==0 ){ zPCode = mprintf("%b", &xfer.aToken[2]); db_set("project-code", zPCode, 0); } if( cloneSeqno>0 ) blob_appendf(&send, "clone 3 %d\n", cloneSeqno); nCardSent++; }else /* config NAME SIZE \n CONTENT ** ** Receive a configuration value from the server. ** ** The received configuration setting is silently ignored if it was ** not requested by a prior "reqconfig" sent from client to server. */ |
︙ | ︙ | |||
1687 1688 1689 1690 1691 1692 1693 | configure_receive(zName, &content, origConfigRcvMask); nCardRcvd++; nArtifactRcvd++; blob_reset(&content); blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR); }else | | | 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 | configure_receive(zName, &content, origConfigRcvMask); nCardRcvd++; nArtifactRcvd++; blob_reset(&content); blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR); }else /* cookie TEXT ** ** The server might include a cookie in its reply. The client ** should remember this cookie and send it back to the server ** in its next query. ** ** Each cookie received overwrites the prior cookie from the |
︙ | ︙ | |||
1729 1730 1731 1732 1733 1734 1735 | /* message MESSAGE ** ** Print a message. Similar to "error" but does not stop processing. ** ** If the "login failed" message is seen, clear the sync password prior ** to the next cycle. | | | | | | | | > > > > > > > | 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 | /* message MESSAGE ** ** Print a message. Similar to "error" but does not stop processing. ** ** If the "login failed" message is seen, clear the sync password prior ** to the next cycle. */ if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){ char *zMsg = blob_terminate(&xfer.aToken[1]); defossilize(zMsg); if( (syncFlags & SYNC_PUSH) && zMsg && sqlite3_strglob("pull only *", zMsg)==0 ){ syncFlags &= ~SYNC_PUSH; zMsg = 0; } if( zMsg && zMsg[0] ){ fossil_force_newline(); fossil_print("Server says: %s\n", zMsg); } }else /* pragma NAME VALUE... ** ** The server can send pragmas to try to convey meta-information to ** the client. These are informational only. Unknown pragmas are ** silently ignored. */ if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){ }else /* error MESSAGE ** ** Report an error and abandon the sync session. ** ** Except, when cloning we will sometimes get an error on the ** first message exchange because the project-code is unknown ** and so the login card on the request was invalid. The project-code ** is returned in the reply before the error card, so second and ** subsequent messages should be OK. Nevertheless, we need to ignore ** the error card on the first message of a clone. */ if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){ if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){ char *zMsg = blob_terminate(&xfer.aToken[1]); defossilize(zMsg); fossil_force_newline(); fossil_print("Error: %s\n", zMsg); if( fossil_strcmp(zMsg, "login failed")==0 ){ if( nCycle<2 ){ g.url.passwd = 0; go = 1; if( g.cgiOutput==0 ){ g.url.flags |= URL_PROMPT_PW; g.url.flags &= ~URL_PROMPTED; url_prompt_for_password(); url_remember(); } }else{ nErr++; } }else{ blob_appendf(&xfer.err, "server says: %s\n", zMsg); nErr++; } break; } |
︙ | ︙ | |||
1812 1813 1814 1815 1816 1817 1818 | if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0 && (configRcvMask & CONFIGSET_OLDFORMAT)!=0 ){ configure_finalize_receive(); } origConfigRcvMask = 0; if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){ | | > | | 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 | if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0 && (configRcvMask & CONFIGSET_OLDFORMAT)!=0 ){ configure_finalize_receive(); } origConfigRcvMask = 0; if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){ fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Received:", blob_size(&recv), nCardRcvd, xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile); }else{ fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, nRoundtrip, nArtifactSent, nArtifactRcvd); } blob_reset(&recv); nCycle++; /* If we received one or more files on the previous exchange but ** there are still phantoms, then go another round. */ |
︙ | ︙ | |||
1838 1839 1840 1841 1842 1843 1844 | } nCardRcvd = 0; xfer.nFileRcvd = 0; xfer.nDeltaRcvd = 0; xfer.nDanglingFile = 0; /* If we have one or more files queued to send, then go | | | > > > > > > > > | | | | > | | | | > | 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 | } nCardRcvd = 0; xfer.nFileRcvd = 0; xfer.nDeltaRcvd = 0; xfer.nDanglingFile = 0; /* If we have one or more files queued to send, then go ** another round */ if( xfer.nFileSent+xfer.nDeltaSent>0 ){ go = 1; } /* If this is a clone, the go at least two rounds */ if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1; /* Stop the cycle if the server sends a "clone_seqno 0" card and ** we have gone at least two rounds. Always go at least two rounds ** on a clone in order to be sure to retrieve the configuration ** information which is only sent on the second round. */ if( cloneSeqno<=0 && nCycle>1 ) go = 0; db_multi_exec("DROP TABLE onremote"); if( go ){ manifest_crosslink_end(MC_PERMIT_HOOKS); }else{ manifest_crosslink_end(MC_PERMIT_HOOKS); content_enable_dephantomize(1); } db_end_transaction(0); }; transport_stats(&nSent, &nRcvd, 1); if( (rSkew*24.0*3600.0) > 10.0 ){ fossil_warning("*** time skew *** server is fast by %s", db_timespan_name(rSkew)); g.clockSkewSeen = 1; }else if( rSkew*24.0*3600.0 < -10.0 ){ fossil_warning("*** time skew *** server is slow by %s", db_timespan_name(-rSkew)); g.clockSkewSeen = 1; } fossil_force_newline(); fossil_print( "%s done, sent: %lld received: %lld ip: %s\n", zOpType, nSent, nRcvd, g.zIpAddr); transport_close(&g.url); transport_global_shutdown(&g.url); if( nErr && go==2 ){ db_multi_exec("DROP TABLE onremote"); manifest_crosslink_end(MC_PERMIT_HOOKS); content_enable_dephantomize(1); db_end_transaction(0); } return nErr; } |
Changes to src/xfersetup.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | /* ** Main sub-menu for configuring the transfer system. ** WEBPAGE: xfersetup */ void xfersetup_page(void){ login_check_credentials(); if( !g.perm.Setup ){ | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | /* ** Main sub-menu for configuring the transfer system. ** WEBPAGE: xfersetup */ void xfersetup_page(void){ login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } style_header("Transfer Setup"); @ <table class="xfersetup"> setup_menu_entry("Common", "xfersetup_com", "Common TH1 code run before all transfer request processing."); setup_menu_entry("Push", "xfersetup_push", "Specific TH1 code to run after \"push\" transfer requests."); setup_menu_entry("Commit", "xfersetup_commit", "Specific TH1 code to run after processing a commit."); setup_menu_entry("Ticket", "xfersetup_ticket", "Specific TH1 code to run after processing a ticket change."); @ </table> url_parse(0, 0); if( g.url.protocol ){ unsigned syncFlags; const char *zButton; char *zWarning; if( db_get_boolean("dont-push", 0) ){ syncFlags = SYNC_PULL; zButton = "Pull"; zWarning = 0; }else{ syncFlags = SYNC_PUSH | SYNC_PULL; zButton = "Synchronize"; zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.", g.url.canonical); } @ <p>Press the <strong>%h(zButton)</strong> button below to @ synchronize with the <em>%h(g.url.canonical)</em> repository now.<br/> @ This may be useful when testing the various transfer scripts.</p> @ <p>You can use the <code>http -async</code> command in your scripts, but @ make sure the <code>th1-uri-regexp</code> setting is set first.</p> if( zWarning ){ @ @ <big><b>%h(zWarning)</b></big> free(zWarning); } @ @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> login_insert_csrf_secret(); @ <input type="submit" name="sync" value="%h(zButton)" /> @ </div></form> @ if( P("sync") ){ user_select(); url_enable_proxy(0); @ <pre class="xfersetup"> client_sync(syncFlags, 0, 0); @ </pre> } } style_footer(); } /* ** Common implementation for the transfer setup editor pages. */ static void xfersetup_generic( const char *zTitle, /* Page title */ const char *zDbField, /* Configuration field being edited */ const char *zDfltValue, /* Default text value */ const char *zDesc, /* Description of this field */ char *(*xText)(const char*), /* Validity test or NULL */ void (*xRebuild)(void), /* Run after successful update */ int height /* Height of the edit box */ ){ const char *z; int isSubmit; login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } if( P("setup") ){ cgi_redirect("xfersetup"); } isSubmit = P("submit")!=0; z = P("x"); if( z==0 ){ |
︙ | ︙ | |||
86 87 88 89 90 91 92 | cgi_redirect("xfersetup"); } } @ <form action="%s(g.zTop)/%s(g.zPath)" method="post"><div> login_insert_csrf_secret(); @ <p>%s(zDesc)</p> @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> | | | < > | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | cgi_redirect("xfersetup"); } } @ <form action="%s(g.zTop)/%s(g.zPath)" method="post"><div> login_insert_csrf_secret(); @ <p>%s(zDesc)</p> @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> @ <p> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="clear" value="Revert To Default" /> @ <input type="submit" name="setup" value="Cancel" /> @ </p> @ </div></form> if ( zDfltValue ){ @ <hr /> @ <h2>Default %s(zTitle)</h2> @ <blockquote><pre> @ %h(zDfltValue) @ </pre></blockquote> } style_footer(); } |
︙ | ︙ | |||
130 131 132 133 134 135 136 | static const char *zDefaultXferPush = 0; /* ** WEBPAGE: xfersetup_push */ void xfersetup_push_page(void){ static const char zDesc[] = | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | static const char *zDefaultXferPush = 0; /* ** WEBPAGE: xfersetup_push */ void xfersetup_push_page(void){ static const char zDesc[] = @ Enter TH1 script that runs after processing <strong>push</strong> @ transfer requests. ; xfersetup_generic( "Transfer Push Script", "xfer-push-script", zDefaultXferPush, zDesc, 0, 0, 30 ); } static const char *zDefaultXferCommit = 0; /* ** WEBPAGE: xfersetup_commit */ void xfersetup_commit_page(void){ static const char zDesc[] = @ Enter TH1 script that runs when a commit is processed. ; xfersetup_generic( "Transfer Commit Script", "xfer-commit-script", zDefaultXferCommit, zDesc, 0, 0, 30 ); } static const char *zDefaultXferTicket = 0; /* ** WEBPAGE: xfersetup_ticket */ void xfersetup_ticket_page(void){ static const char zDesc[] = @ Enter TH1 script that runs when a ticket change is processed. ; xfersetup_generic( "Transfer Ticket Script", "xfer-ticket-script", zDefaultXferTicket, zDesc, 0, 0, 30 ); } |
Changes to src/zip.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to generate ZIP archives. */ #include <assert.h> | > > > > > | < > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to generate ZIP archives. */ #include "config.h" #include <assert.h> #if defined(FOSSIL_ENABLE_MINIZ) # define MINIZ_HEADER_FILE_ONLY # include "miniz.c" #else # include <zlib.h> #endif #include "zip.h" /* ** Write a 16- or 32-bit integer as little-endian into the given buffer. */ static void put16(char *z, int v){ z[0] = v & 0xff; |
︙ | ︙ | |||
79 80 81 82 83 84 85 | /* ** Set the date and time from a julian day number. */ void zip_set_timedate(double rDate){ char *zDate = db_text(0, "SELECT datetime(%.17g)", rDate); zip_set_timedate_from_str(zDate); | | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | /* ** Set the date and time from a julian day number. */ void zip_set_timedate(double rDate){ char *zDate = db_text(0, "SELECT datetime(%.17g)", rDate); zip_set_timedate_from_str(zDate); fossil_free(zDate); unixTime = (rDate - 2440587.5)*86400.0; } /* ** If the given filename includes one or more directory entries, make ** sure the directories are already in the archive. If they are not ** in the archive, add them. |
︙ | ︙ | |||
156 157 158 159 160 161 162 | put16(&zHdr[4], 0x000a); put16(&zHdr[6], 0x0800); put16(&zHdr[8], iMethod); put16(&zHdr[10], dosTime); put16(&zHdr[12], dosDate); put16(&zHdr[26], nameLen); put16(&zHdr[28], 13); | | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | put16(&zHdr[4], 0x000a); put16(&zHdr[6], 0x0800); put16(&zHdr[8], iMethod); put16(&zHdr[10], dosTime); put16(&zHdr[12], dosDate); put16(&zHdr[26], nameLen); put16(&zHdr[28], 13); put16(&zExTime[0], 0x5455); put16(&zExTime[2], 9); zExTime[4] = 3; put32(&zExTime[5], unixTime); put32(&zExTime[9], unixTime); /* Write the header and filename. */ iStart = blob_size(&body); blob_append(&body, zHdr, 30); blob_append(&body, zName, nameLen); blob_append(&body, zExTime, 13); |
︙ | ︙ | |||
200 201 202 203 204 205 206 | deflate(&stream, Z_FINISH); toOut = sizeof(zOutBuf) - stream.avail_out; blob_append(&body, zOutBuf, toOut); }while( stream.avail_out==0 ); nByte = stream.total_in; nByteCompr = stream.total_out; deflateEnd(&stream); | | | | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | deflate(&stream, Z_FINISH); toOut = sizeof(zOutBuf) - stream.avail_out; blob_append(&body, zOutBuf, toOut); }while( stream.avail_out==0 ); nByte = stream.total_in; nByteCompr = stream.total_out; deflateEnd(&stream); /* Go back and write the header, now that we know the compressed file size. */ z = &blob_buffer(&body)[iStart]; put32(&z[14], iCRC); put32(&z[18], nByteCompr); put32(&z[22], nByte); } /* Make an entry in the tables of contents */ memset(zBuf, 0, sizeof(zBuf)); put32(&zBuf[0], 0x02014b50); put16(&zBuf[4], 0x0317); put16(&zBuf[6], 0x000a); put16(&zBuf[8], 0x0800); |
︙ | ︙ | |||
265 266 267 268 269 270 271 | put16(&zBuf[20], 0); blob_append(&body, zBuf, 22); blob_reset(&toc); *pZip = body; blob_zero(&body); nEntry = 0; for(i=0; i<nDir; i++){ | | | | 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | put16(&zBuf[20], 0); blob_append(&body, zBuf, 22); blob_reset(&toc); *pZip = body; blob_zero(&body); nEntry = 0; for(i=0; i<nDir; i++){ fossil_free(azDir[i]); } fossil_free(azDir); nDir = 0; azDir = 0; } /* ** COMMAND: test-filezip ** |
︙ | ︙ | |||
320 321 322 323 324 325 326 | */ void zip_of_baseline(int rid, Blob *pZip, const char *zDir){ Blob mfile, hash, file; Manifest *pManifest; ManifestFile *pFile; Blob filename; int nPrefix; | | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | */ void zip_of_baseline(int rid, Blob *pZip, const char *zDir){ Blob mfile, hash, file; Manifest *pManifest; ManifestFile *pFile; Blob filename; int nPrefix; content_get(rid, &mfile); if( blob_size(&mfile)==0 ){ blob_zero(pZip); return; } blob_zero(&hash); blob_zero(&filename); |
︙ | ︙ | |||
343 344 345 346 347 348 349 | if( pManifest ){ char *zName; zip_set_timedate(pManifest->rDate); if( db_get_boolean("manifest", 0) ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); zip_add_folders(zName); | < > > | 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | if( pManifest ){ char *zName; zip_set_timedate(pManifest->rDate); if( db_get_boolean("manifest", 0) ){ blob_append(&filename, "manifest", -1); zName = blob_str(&filename); zip_add_folders(zName); sha1sum_blob(&mfile, &hash); sterilize_manifest(&mfile); zip_add_file(zName, &mfile, 0); blob_reset(&mfile); blob_append(&hash, "\n", 1); blob_resize(&filename, nPrefix); blob_append(&filename, "manifest.uuid", -1); zName = blob_str(&filename); zip_add_file(zName, &hash, 0); blob_reset(&hash); |
︙ | ︙ | |||
391 392 393 394 395 396 397 398 399 400 401 402 403 404 | */ void baseline_zip_cmd(void){ int rid; Blob zip; const char *zName; zName = find_option("name", 0, 1); db_find_and_open_repository(0, 0); if( g.argc!=4 ){ usage("VERSION OUTPUTFILE"); } rid = name_to_typed_rid(g.argv[2],"ci"); if( zName==0 ){ zName = db_text("default-name", "SELECT replace(%Q,' ','_') " | > > > > | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | */ void baseline_zip_cmd(void){ int rid; Blob zip; const char *zName; zName = find_option("name", 0, 1); db_find_and_open_repository(0, 0); /* We should be done with options.. */ verify_all_options(); if( g.argc!=4 ){ usage("VERSION OUTPUTFILE"); } rid = name_to_typed_rid(g.argv[2],"ci"); if( zName==0 ){ zName = db_text("default-name", "SELECT replace(%Q,' ','_') " |
︙ | ︙ | |||
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | /* ** WEBPAGE: zip ** URL: /zip/RID.zip ** ** Generate a ZIP archive for the baseline. ** Return that ZIP archive as the HTTP reply content. */ void baseline_zip_page(void){ int rid; char *zName, *zRid; int nName, nRid; Blob zip; login_check_credentials(); | > > > > > > > > > > > > | > > > > > > > > | | | | > > > > > > > > > > > > > > > | > > | | > | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | /* ** WEBPAGE: zip ** URL: /zip/RID.zip ** ** Generate a ZIP archive for the baseline. ** Return that ZIP archive as the HTTP reply content. ** ** Optional URL Parameters: ** ** - name=NAME[.zip] is the name of the output file. Defaults to ** something project/version-specific. The base part of the ** name, up to the last dot, is used as the top-most directory ** name in the output file. ** ** - uuid=the version to zip (may be a tag/branch name). ** Defaults to "trunk". ** */ void baseline_zip_page(void){ int rid; char *zName, *zRid; int nName, nRid; Blob zip; char *zKey; login_check_credentials(); if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; } load_control(); zName = mprintf("%s", PD("name","")); nName = strlen(zName); zRid = mprintf("%s", PD("uuid","trunk")); nRid = strlen(zRid); if( nName>4 && fossil_strcmp(&zName[nName-4], ".zip")==0 ){ /* Special case: Remove the ".zip" suffix. */ nName -= 4; zName[nName] = 0; }else{ /* If the file suffix is not ".zip" then just remove the ** suffix up to and including the last "." */ for(nName=strlen(zName)-1; nName>5; nName--){ if( zName[nName]=='.' ){ zName[nName] = 0; break; } } } rid = name_to_typed_rid(nRid?zRid:zName,"ci"); if( rid==0 ){ @ Not found return; } if( referred_from_login() ){ style_header("ZIP Archive Download"); @ <form action='%R/zip'> cgi_query_parameters_to_hidden(); @ <p>ZIP Archive named <b>%h(zName).zip</b> holding the content @ of check-in <b>%h(zRid)</b>: @ <input type="submit" value="Download" /> @ </form> style_footer(); return; } if( nRid==0 && nName>10 ) zName[10] = 0; zKey = db_text(0, "SELECT '/zip/'||uuid||'/%q' FROM blob WHERE rid=%d",zName,rid); blob_zero(&zip); if( cache_read(&zip, zKey)==0 ){ zip_of_baseline(rid, &zip, zName); cache_write(&zip, zKey); } fossil_free( zName ); fossil_free( zRid ); fossil_free( zKey ); cgi_set_content(&zip); cgi_set_content_type("application/zip"); } |
Added test/comment.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | # # Copyright (c) 2014 D. Richard Hipp # # This program is free software; you can redistribute it and/or # modify it under the terms of the Simplified BSD License (also # known as the "2-Clause License" or "FreeBSD License".) # # This program is distributed in the hope that it will be useful, # but without any warranty; without even the implied warranty of # merchantability or fitness for a particular purpose. # # Author contact information: # drh@hwaci.com # http://www.hwaci.com/drh/ # ############################################################################ # # Test comment formatting and printing. # fossil test-comment-format "" "" test comment-1 {$RESULT eq "\n(1 lines output)"} ############################################################################### fossil test-comment-format --decode "" "" test comment-2 {$RESULT eq "\n(1 lines output)"} ############################################################################### fossil test-comment-format --width 26 " " "this is a short comment." test comment-3 {$RESULT eq " this is a short comment.\n(1 lines output)"} ############################################################################### fossil test-comment-format --width 26 --decode " " "this is a short comment." test comment-4 {$RESULT eq " this is a short comment.\n(1 lines output)"} ############################################################################### fossil test-comment-format --width 26 "*PREFIX* " "this is a short comment." test comment-5 {$RESULT eq "*PREFIX* this is a short c\n omment.\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 26 --decode "*PREFIX* " "this is a short comment." test comment-6 {$RESULT eq "*PREFIX* this is a short c\n omment.\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 26 "" "this\\sis\\sa\\sshort\\scomment." test comment-7 {$RESULT eq "this\\sis\\sa\\sshort\\scommen\nt.\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 26 --decode "" "this\\sis\\sa\\sshort\\scomment." test comment-8 {$RESULT eq "this is a short comment.\n(1 lines output)"} ############################################################################### fossil test-comment-format --width 78 --decode --trimspace "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly." test comment-9 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test is\n working correctly.\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 78 --decode --trimspace "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly. more text here describing the issue.\\nanother line here..................................................................................*" test comment-10 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test is\n working correctly. more text here describing the issue.\n another line here....................................................\n ..............................*\n(4 lines output)"} ############################################################################### fossil test-comment-format --width 78 "HH:MM:SS " "....................................................................................*" test comment-11 {$RESULT eq "HH:MM:SS .....................................................................\n ...............*\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 78 "HH:MM:SS " ".....................................................................*" 78 test comment-12 {$RESULT eq "HH:MM:SS .....................................................................\n *\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 26 "*TEST* " "this\tis a test." test comment-13 {$RESULT eq "*TEST* this\tis a te\n st.\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 60 "*TEST* " "this is a test......................................................................................................................." test comment-14 {$RESULT eq "*TEST* this is a test.......................................\n .....................................................\n ...........................\n(3 lines output)"} ############################################################################### fossil test-comment-format --width 60 --wordbreak "*TEST* " "this is a test......................................................................................................................." test comment-15 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} ############################################################################### fossil test-comment-format --width 60 "*TEST* " "this is a test......................................................................................................................." test comment-16 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} ############################################################################### fossil test-comment-format --width 60 --wordbreak "*TEST* " "this is a test......................................................................................................................." test comment-17 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} ############################################################################### fossil test-comment-format --width 60 "*TEST* " "one two three four five six seven eight nine ten eleven twelve" test comment-18 {$RESULT eq "*TEST* one two three four five six seven eight nine ten elev\n en twelve\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 60 --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" test comment-19 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 60 "*TEST* " "one two three four five six seven eight nine ten eleven twelve" test comment-20 {$RESULT eq "*TEST* one two three four five\n six seven eight nine ten\n eleven twelve\n(3 lines output)"} ############################################################################### fossil test-comment-format --width 60 --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" test comment-21 {$RESULT eq "*TEST* one two three four five\n six seven eight nine ten\n eleven twelve\n(3 lines output)"} ############################################################################### fossil test-comment-format --legacy "" "" test comment-22 {$RESULT eq "\n(1 lines output)"} ############################################################################### fossil test-comment-format --legacy --decode "" "" test comment-23 {$RESULT eq "\n(1 lines output)"} ############################################################################### fossil test-comment-format --width 26 --legacy " " "this is a short comment." test comment-24 {$RESULT eq " this is a short comment.\n(1 lines output)"} ############################################################################### fossil test-comment-format --width 26 --legacy --decode " " "this is a short comment." test comment-25 {$RESULT eq " this is a short comment.\n(1 lines output)"} ############################################################################### fossil test-comment-format --width 25 --legacy "*PREFIX* " "this is a short comment." test comment-26 {$RESULT eq "*PREFIX* this is a short\n comment.\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 25 --legacy --decode "*PREFIX* " "this is a short comment." test comment-27 {$RESULT eq "*PREFIX* this is a short\n comment.\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 26 --legacy "" "this\\sis\\sa\\sshort\\scomment." test comment-28 {$RESULT eq "this\\sis\\sa\\sshort\\scommen\nt.\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 26 --legacy --decode "" "this\\sis\\sa\\sshort\\scomment." test comment-29 {$RESULT eq "this is a short comment.\n(1 lines output)"} ############################################################################### fossil test-comment-format --width 78 --legacy --decode "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly." test comment-30 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test\n is working correctly.\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 78 --legacy --decode "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly. more text here describing the issue.\\nanother line here..................................................................................*" test comment-31 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test\n is working correctly. more text here describing the issue. another\n line\n here.................................................................\n .................*\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 78 --legacy "HH:MM:SS " "....................................................................................*" test comment-32 {$RESULT eq "HH:MM:SS .....................................................................\n ...............*\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 78 --legacy "HH:MM:SS " ".....................................................................*" test comment-33 {$RESULT eq "HH:MM:SS .....................................................................\n *\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 26 --legacy "*TEST* " "this\tis a test." test comment-34 {$RESULT eq "*TEST* this is a test.\n(1 lines output)"} ############################################################################### fossil test-comment-format --width 60 --legacy "*TEST* " "this is a test......................................................................................................................." test comment-35 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} ############################################################################### fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "this is a test......................................................................................................................." test comment-36 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} ############################################################################### fossil test-comment-format --width 60 --legacy "*TEST* " "this is a test......................................................................................................................." test comment-37 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} ############################################################################### fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "this is a test......................................................................................................................." test comment-38 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} ############################################################################### fossil test-comment-format --width 60 --legacy "*TEST* " "one two three four five six seven eight nine ten eleven twelve" test comment-39 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" test comment-40 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 60 --legacy "*TEST* " "one two three four five six seven eight nine ten eleven twelve" test comment-41 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} ############################################################################### fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" test comment-42 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} ############################################################################### set orig "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\\nxxxxxxx." fossil test-comment-format --width 73 --decode --origbreak "" $orig test comment-43 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 73 --decode --origbreak "" $orig $orig test comment-44 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 73 --decode --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig test comment-45 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(6 lines output)"} ############################################################################### fossil test-comment-format --width 82 --indent 9 --decode --origbreak " " $orig test comment-46 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 82 --indent 9 --decode --origbreak " " $orig $orig test comment-47 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 82 --indent 9 --decode --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig test comment-48 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} ############################################################################### fossil test-comment-format --width 72 --decode --trimspace --origbreak "" $orig test comment-49 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 72 --decode --trimspace --origbreak "" $orig $orig test comment-50 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 72 --decode --trimspace --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig test comment-51 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(6 lines output)"} ############################################################################### fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak " " $orig test comment-52 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak " " $orig $orig test comment-53 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig test comment-54 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} ############################################################################### fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" $orig test comment-55 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" $orig $orig test comment-56 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig test comment-57 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(6 lines output)"} ############################################################################### fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak " " $orig test comment-58 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak " " $orig $orig test comment-59 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} ############################################################################### fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig test comment-60 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} |
Changes to test/diff-test-1.wiki.
︙ | ︙ | |||
15 16 17 18 19 20 21 | * <a href="../../../fdiff?v1=d1c60722e0b9d775&v2=58d1a8991bacb113" target="testwindow">Column alignment with multibyte characters.</a> The edit of a line with multibyte characters is the first chunk. * <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6" target="testwindow">Large diff of sqlite3.c</a>. This diff was very slow prior to the performance enhancement change [9e15437e97]. * <a href="../../../info/bda00cbada#chunk49" target="testwindow"> | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | * <a href="../../../fdiff?v1=d1c60722e0b9d775&v2=58d1a8991bacb113" target="testwindow">Column alignment with multibyte characters.</a> The edit of a line with multibyte characters is the first chunk. * <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6" target="testwindow">Large diff of sqlite3.c</a>. This diff was very slow prior to the performance enhancement change [9e15437e97]. * <a href="../../../info/bda00cbada#chunk49" target="testwindow"> A difficult indentation change.</a> (show-version-diffs must be enabled) * <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk13" target="testwindow">Another tricky indentation.</a> Notice especially lines 59398 and 59407 on the left. * <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk13" target="testwindow">Inverse of the previous.</a> * <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk24" target="testwindow">A complex change</a> that is difficult to align, and |
︙ | ︙ |
Changes to test/file1.test.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | set i 1 foreach {path result} $args { fossil test-simplify-name $path test simplify-name-$testname.$i {$::RESULT=="\[$path\] -> \[$result\]"} incr i } } simplify-name 100 . . .// . .. .. ..///// .. simplify-name 101 {} {} / / ///////// / ././././ . simplify-name 102 x x /x /x ///x //x simplify-name 103 a/b a/b /a/b /a/b a///b a/b ///a///b///// //a/b simplify-name 104 a/b/../c/ a/c /a/b/../c /a/c /a/b//../c /a/c /a/b/..///c /a/c simplify-name 105 a/b/../../x/y x/y /a/b/../../x/y /x/y simplify-name 106 a/b/../../../x/y ../x/y /a/b/../../../x/y /../x/y simplify-name 107 a/./b/.././../x/y x/y a//.//b//..//.//..//x//y/// x/y | > > > > > > > > > > > > > > > > > > > > > > > > > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | set i 1 foreach {path result} $args { fossil test-simplify-name $path test simplify-name-$testname.$i {$::RESULT=="\[$path\] -> \[$result\]"} incr i } } proc relative-name {testname args} { set i 1 foreach {subdir path result} $args { fossil test-relative-name --chdir $subdir $path test relative-name-$testname.$i {$::RESULT==$result} incr i } } simplify-name 100 . . .// . .. .. ..///// .. simplify-name 101 {} {} / / ///////// / ././././ . simplify-name 102 x x /x /x ///x //x simplify-name 103 a/b a/b /a/b /a/b a///b a/b ///a///b///// //a/b simplify-name 104 a/b/../c/ a/c /a/b/../c /a/c /a/b//../c /a/c /a/b/..///c /a/c simplify-name 105 a/b/../../x/y x/y /a/b/../../x/y /x/y simplify-name 106 a/b/../../../x/y ../x/y /a/b/../../../x/y /../x/y simplify-name 107 a/./b/.././../x/y x/y a//.//b//..//.//..//x//y/// x/y if {$::tcl_platform(os)=="Windows NT"} { simplify-name 108 //?/a:/a/b a:/a/b //?/UNC/a/b //a/b //?/ {} simplify-name 109 \\\\?\\a:\\a\\b a:/a/b \\\\?\\UNC\\a\\b //a/b \\\\?\\ {} } # Those directories are only needed for the testcase being able to "--chdir" to it. file mkdir test1 file mkdir test1/test2 relative-name 100 . . . test1 [pwd] .. test1 [pwd]/ .. test1 [pwd]/test ../test relative-name 101 test1/test2 [pwd] ../.. test1/test2 [pwd]/ ../.. test1/test2 [pwd]/test ../../test relative-name 102 test1 [pwd]/test ../test . [pwd]/file1 ./file1 . [pwd]/file1/file2 ./file1/file2 catch {file delete test1/test2} catch {file delete test1} |
Added test/fileStat.th1.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | <th1> proc doSomeTclSetup {} { # # NOTE: Copy repository file name to the Tcl interpreter. This is # done first (once) because it will be necessary for almost # everything else later on. # tclInvoke set repository [repository] # # NOTE: Create some procedures in the Tcl interpreter to perform # useful operations. This could also do things like load # packages, etc. # tclEval { # # NOTE: Returns an [exec] command for Fossil, using the provided # sub-command and arguments, suitable for use with [eval] # or [catch]. # proc getFossilCommand { repository user args } { global env lappend result exec [info nameofexecutable] if {[info exists env(GATEWAY_INTERFACE)]} then { # # NOTE: This option is required when calling # out to the Fossil executable from a # CGI process. # lappend result -nocgi } eval lappend result $args if {[string length $repository] > 0} then { # # NOTE: This is almost certainly required # when calling out to the Fossil # executable on the server because # there is almost never an open # checkout. # lappend result -R $repository } if {[string length $user] > 0} then { lappend result -U $user } # th1Eval [list html $result<br>] return $result } } } proc getLatestTrunkCheckIn {} { tclEval { # # NOTE: Get the unique Id of the latest check-in on trunk. # return [lindex [regexp -line -inline -nocase -- \ {^uuid:\s+([0-9A-F]{40}) } [eval [getFossilCommand \ $repository "" info trunk]]] end] } } proc theSumOfAllFiles { id } { # # NOTE: Copy check-in Id value to the Tcl interpreter. # tclInvoke set id $id tclEval { set count 0 foreach line [split [eval [getFossilCommand \ $repository "" artifact $id]] \n] { # # NOTE: Is this an "F" (file) card? # if {[string range $line 0 1] eq "F "} then { incr count } } return $count } } doSomeTclSetup; # perform some extra setup for the Tcl interpreter. set checkIn [getLatestTrunkCheckIn] set totalFiles [theSumOfAllFiles $checkIn] </th1> <br /> As of trunk check-in <th1>decorate \[$checkIn\]</th1>, this repository contains <th1>html $totalFiles</th1> files. <br /> |
Added test/fileage-test-1.wiki.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | This page contains URLs for file-age computations that have given trouble in the past. Shif-click on on the links, one-by-one, to verify that the current implementation works correctly: * [/fileage?name=c9df0dcdaa402] - Verify that the many execute permission changes that occurred about 24 hours before check-in c9df0dcdaa402 do not appear as file changes. * [/tree?ci=c9df0dcdaa40&mtime=0&type=tree] - Verify that all three skin files (css.txt, footer.txt, and header.txt) appear in all of the skin/*/ folders. * On both of the above, check for excessive computation time. |
Changes to test/graph-test-1.wiki.
1 2 3 | <title>Graph Test One</title> This page contains examples a list of URLs of timelines with | | | | | | | < < | | | | | | | > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | <title>Graph Test One</title> This page contains examples a list of URLs of timelines with interesting graphs. Click on all URLs, one by one, to verify the correct operation of the graph drawing logic. * <a href="../../../timeline?n=20&y=ci&b=2010-11-08" target="testwindow"> 20-element timeline, check-ins only, before 2010-11-08</a> * <a href="../../../timeline?n=20&y=ci&b=2010-11-08&ng" target="testwindow"> 20-element timeline, check-ins only, no graph, before 2010-11-08</a> * <a href="../../../timeline?n=20&y=ci&b=2010-11-08&fc" target="testwindow"> 20-element timeline, check-ins only, file changes, before 2010-11-08</a> * <a href="../../../timeline?n=40&y=ci&b=2010-11-08" target="testwindow"> 40-element timeline, check-ins only, before 2010-11-08</a> * <a href="../../../timeline?n=1000&y=ci&b=2010-11-08" target="testwindow"> 1000-element timeline, check-ins only, before 2010-11-08</a> * <a href="../../../timeline?n=10&c=2010-11-07T10:23:00" target="testwindow"> 10-elements circa 2010-11-07 10:23:00, with dividers</a> * <a href="../../../timeline?n=10&c=2010-11-07T10:23:00&nd" target="testwindow"> 10-elements circa 2010-11-07 10:23:00, without dividers</a> * <a href="../../../timeline?f=3ea66260b5555" target="testwindow"> Parents and children of check-in 3ea66260b5555</a> * <a href="../../../timeline?d=e5fe4164f74a7576&p=e5fe4164f74a7576&n=3" target="testwindow">multiple merge descenders from the penultimate node. </a> * <a href="../../../timeline?y=ci&a=2010-12-20" target="testwindow"> multiple branch risers.</a> * <a href="../../../timeline?y=ci&a=2010-12-20&n=18" target="testwindow"> multiple branch risers, n=18.</a> * <a href="../../../timeline?y=ci&a=2010-12-20&n=9" target="testwindow"> multiple branch risers, n=9.</a> * <a href="../../../timeline?r=experimental&n=0" target="testwindow"> Experimental branch with related check-ins.</a> * <a href="../../../timeline?r=experimental&mionly&n=0" target="testwindow"> Experimental branch with merge-ins only.</a> * <a href="../../../timeline?t=experimental&n=0" target="testwindow"> Experimental branch check-ins only.</a> * <a href="../../../timeline?r=release&n=0" target="testwindow"> Check-ins tagged "release" and related check-ins</a> * <a href="../../../timeline?r=release&mionly&n=0" target="testwindow"> Check-ins tagged "release" and merge-ins</a> * <a href="../../../timeline?t=release&n=0" target="testwindow"> Only check-ins tagged "release"</a> * <a href="../../../finfo?name=Makefile" target="testwindow"> History of source file "Makefile".</a> * <a href="../../../timeline?a=1970-01-01" target="testwindow"> 20 elements after 1970-01-01.</a> * <a href="../../../timeline?n=100000000&y=ci" target="testwindow"> All check-ins - a huge graph.</a> * <a href="../../../timeline?f=8dfed953f7530442" target="testwindow"> This malformed commit has a merge parent which is not a valid checkin.</a> * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9&shortest" target="testwindow"> From e663bac6f7 to a298a0e2f9 by shortest path.</a> * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9" target="testwindow"> From e663bac6f7 to a298a0e2f9 without merge links.</a> * <a href="../../../timeline?me=e663bac6f7&you=a298a0e2f9" target="testwindow"> Common ancestor path of e663bac6f7 to a298a0e2f9.</a> * <a href="../../../timeline?f=65dd90fb95a2af55" target="testwindow"> Merge on the same branch does not result in a leaf. </a> * <a href="../../../timeline?c=20015206bc" target="testwindow"> This timeline has a hidden commit.</a> Click Unhide to reveal. * <a href="../../../timeline?y=ci&n=15&b=2a4e4cf03e" target="testwindow">Isolated check-ins.</a> External: * <a href="http://www.sqlite.org/src/timeline?c=2010-09-29&nd" target="testwindow">Timewarp due to a mis-configured system clock.</a> * <a href="http://core.tcl.tk/tk/finfo?name=tests/id.test" target="testwindow">Show all three separate deletions of "id.test". (Scroll down for the third deletion.) * <a href='http://core.tcl.tk/tk/timeline?y=ci&b=2015-03-07' target='testwindow'>Merge arrows to the left and to the right</a> * <a href='http://core.tcl.tk/tk/timeline?y=ci&b=2015-03-07&railpitch=13' target='testwindow'>Previous, with a scrunched graph</a> * <a href='http://core.tcl.tk/tk/timeline?y=ci&b=2015-03-07&railpitch=11' target='testwindow'>Previous, with a severely scrunched graph</a> |
Changes to test/merge2.test.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # Tests of the delta mechanism. # set filelist [glob $testdir/*] foreach f $filelist { if {[file isdir $f]} continue set base [file root [file tail $f]] set f1 [read_file $f] write_file t1 $f1 for {set i 0} {$i<100} {incr i} { expr {srand($i*2)} write_file t2 [set f2 [random_changes $f1 2 4 0 0.1]] expr {srand($i*2+1)} write_file t3 [set f3 [random_changes $f1 2 4 2 0.1]] | > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # Tests of the delta mechanism. # set filelist [glob $testdir/*] foreach f $filelist { if {[file isdir $f]} continue set base [file root [file tail $f]] if {[string match "utf16*" $base]} continue set f1 [read_file $f] write_file t1 $f1 for {set i 0} {$i<100} {incr i} { expr {srand($i*2)} write_file t2 [set f2 [random_changes $f1 2 4 0 0.1]] expr {srand($i*2+1)} write_file t3 [set f3 [random_changes $f1 2 4 2 0.1]] |
︙ | ︙ |
Changes to test/merge5.test.
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 | set env(HOME) [pwd] # Construct a test repository # exec sqlite3 m5.fossil <$testdir/${testfile}_repo.sql fossil rebuild m5.fossil fossil open m5.fossil fossil update baseline checkout-test 10 { da5c8346496f3421cb58f84b6e59e9531d9d424d one.txt ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4 three.txt 278a402316510f6ae4a77186796a6bde78c7dbc1 two.txt } | > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | set env(HOME) [pwd] # Construct a test repository # exec sqlite3 m5.fossil <$testdir/${testfile}_repo.sql fossil rebuild m5.fossil fossil open m5.fossil fossil user default drh --user drh fossil update baseline checkout-test 10 { da5c8346496f3421cb58f84b6e59e9531d9d424d one.txt ed24d19d726d173f18dbf4a9a0f8514daa3e3ca4 three.txt 278a402316510f6ae4a77186796a6bde78c7dbc1 two.txt } |
︙ | ︙ |
Added test/merge6.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | # # Copyright (c) 2014 D. Richard Hipp # # This program is free software; you can redistribute it and/or # modify it under the terms of the Simplified BSD License (also # known as the "2-Clause License" or "FreeBSD License".) # # This program is distributed in the hope that it will be useful, # but without any warranty; without even the implied warranty of # merchantability or fitness for a particular purpose. # # Author contact information: # drh@hwaci.com # http://www.hwaci.com/drh/ # ############################################################################ # # Tests of the "merge" command # #################################################################### # TEST 1: Handle multiple merges each with one or more ADDED files # #################################################################### repo_init fossil ls test merge_multi-0 {[string map [list \r\n \n] [string trim $RESULT]] eq {}} write_file f1 "f1 line" fossil add f1 fossil commit -m "base file" fossil ls test merge_multi-1 {[string map [list \r\n \n] [string trim $RESULT]] eq {f1}} fossil update trunk write_file f2 "f2 line" fossil add f2 fossil commit -m "branch for file f2" -b branch_for_f2 fossil ls test merge_multi-2 {[string map [list \r\n \n] [string trim $RESULT]] eq {f1 f2}} fossil update trunk write_file f3 "f3 line" write_file f4 "f4 line" fossil add f3 fossil add f4 fossil commit -m "branch for files f3 and f4" -b branch_for_f3_f4 fossil ls test merge_multi-3 {[string map [list \r\n \n] [string trim $RESULT]] eq {f1 f3 f4}} fossil update trunk fossil merge branch_for_f2 fossil merge branch_for_f3_f4 fossil commit -m "new trunk files f2, f3, and f4 via merge" fossil ls test merge_multi-4 {[string map [list \r\n \n] [string trim $RESULT]] eq {f1 f2 f3 f4}} |
Changes to test/merge_renames.test.
1 2 | # # Tests for merging with renames | | < < < < < < | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # # Tests for merging with renames # # catch {exec $::fossilexe info} res puts res=$res if {![regexp {use --repository} $res]} { puts stderr "Cannot run this test within an open checkout" return } ###################################### # Test 1 # # Reported: Ticket [554f44ee74e3d] # ###################################### repo_init write_file f1 "line" fossil add f1 fossil commit -m "c1" fossil tag add pivot current write_file f1 "line2" |
︙ | ︙ | |||
42 43 44 45 46 47 48 | fossil commit -m "c4" write_file f1 "line6" fossil commit -m "c4" fossil update pivot fossil mv f1 f2 | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | fossil commit -m "c4" write_file f1 "line6" fossil commit -m "c4" fossil update pivot fossil mv f1 f2 file rename -force f1 f2 fossil commit -b rename -m "c5" fossil merge trunk fossil commit -m "trunk merged" fossil update pivot write_file f3 "someline" |
︙ | ︙ | |||
71 72 73 74 75 76 77 | # failed protOut "Error, the merge should not delete any file" test merge_renames-1 0 } else { test merge_renames-1 1 } | > > > > > > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | # failed protOut "Error, the merge should not delete any file" test merge_renames-1 0 } else { test merge_renames-1 1 } ###################################### # Test 2 # # Reported: Ticket [74413366fe5067] # ###################################### repo_init write_file f1 "line" fossil add f1 fossil commit -m "base file" fossil tag add pivot current write_file f2 "line2" fossil add f2 fossil commit -m "newfile" fossil mv f2 f2new file rename -force f2 f2new fossil commit -m "rename" fossil update pivot write_file f1 "line3" fossil commit -b branch -m "change" fossil merge trunk fossil commit -m "trunk merged" fossil update trunk fossil merge branch puts $RESULT # Not a nice way to check, but I don't know more tcl now set deletes 0 foreach {status filename} $RESULT { if {$status=="DELETE"} { set deletes [expr $deletes + 1] } } if {$deletes!=0} { # failed protOut "Error, the merge should not delete any file" test merge_renames-2 0 } else { test merge_renames-2 1 } ###################################### # Test 3 # # Reported: Ticket [30b28cf351] # ###################################### repo_init write_file f1 "line" fossil add f1 fossil commit -m "base file" fossil tag add pivot current write_file f2 "line2" fossil add f2 fossil commit -m "newfile" fossil mv f2 f2new file rename -force f2 f2new fossil commit -m "rename" fossil update pivot write_file f1 "line3" fossil commit -b branch -m "change" fossil merge trunk |
︙ | ︙ | |||
123 124 125 126 127 128 129 | # failed protOut "Error, the merge should not delete any file" test merge_renames-2 0 } else { test merge_renames-2 1 } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | # failed protOut "Error, the merge should not delete any file" test merge_renames-2 0 } else { test merge_renames-2 1 } ###################################### # Test 4 # # Reported: Ticket [67176c3aa4] # ###################################### # TO BE WRITTEN. ###################################### # Test 5 # # Handle Rename/Add via Merge # ###################################### repo_init write_file f1 "old f1 line" fossil add f1 fossil commit -m "base file" write_file f3 "f3 line" fossil add f3 fossil commit -m "branch file" -b branch_for_f3 fossil update trunk fossil mv f1 f2 file rename -force f1 f2 write_file f1 "new f1 line" fossil add f1 fossil commit -m "rename and add file with old name" fossil update branch_for_f3 fossil merge trunk fossil commit -m "trunk merged, should have 3 files" fossil ls test merge_renames-5 {[string map [list \r\n \n] [string trim $RESULT]] eq {f1 f2 f3}} ###################################### # # Tests for troubles not specifically linked with renames but that I'd like to # write: # [c26c63eb1b] - 'merge --backout' does not handle conflicts properly # [953031915f] - Lack of warning when overwriting extra files # [4df5f38f1e] - Troubles merging a file delete with a file change |
Changes to test/release-checklist.wiki.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | rendered correctly. <li><p> Click on each of the links in in the [./diff-test-1.wiki] document and verify that all diffs are rendered correctly. <li><p> Verify correct name-change tracking behavior (no net changes) for: <blockquote><b> fossil test-name-changes --debug b120bc8b262ac 374920b20944b </b></blockquote> <li><p> | > > > > > > > > > > > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | rendered correctly. <li><p> Click on each of the links in in the [./diff-test-1.wiki] document and verify that all diffs are rendered correctly. <li><p> Click on the following link to verify that it works: [./test-page%2b%2b.wiki | ./test-page++.wiki] (NB: Many web servers automatically block or rewrite URLs that contain "+" characters, even when those "+" characters are encoded as "%2B". On such web servers, the URL above will not work. This test is only guaranteed to work when running "fossil ui".) <li><p> Shift-click on each of the links in [./fileage-test-1.wiki] and verify correct operation of the file-age computation. <li><p> Verify correct name-change tracking behavior (no net changes) for: <blockquote><b> fossil test-name-changes --debug b120bc8b262ac 374920b20944b </b></blockquote> <li><p> |
︙ | ︙ |
Changes to test/revert.test.
1 2 3 4 5 | # # Tests for 'fossil revert' # # | < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < < < | > | | | | < < < < > < > | > > > > | | < | | | > > > > > > | > > | > > | > > | > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | # # Tests for 'fossil revert' # # # Test 'fossil revert' against expected results from 'fossil changes' and # 'fossil addremove -n', as well as by verifying the existence of files # on the file system. 'fossil undo' is called after each test # proc revert-test {testid revertArgs expectedRevertOutput args} { global RESULT set passed 1 set args [dict merge { -changes {} -addremove {} -exists {} -notexists {} } $args] set result [fossil revert {*}$revertArgs] test_status_list revert-$testid $result $expectedRevertOutput set statusListTests [list -changes changes -addremove {addremove -n}] foreach {key fossilArgs} $statusListTests { set expected [dict get $args $key] set result [fossil {*}$fossilArgs] test_status_list revert-$testid$key $result $expected } set fileExistsTests [list -exists 1 does -notexists 0 should] foreach {key expected verb} $fileExistsTests { foreach path [dict get $args $key] { if {[file exists $path] != $expected} { set passed 0 protOut " Failure: File $verb not exist: $path" } } test revert-$testid$key $passed } fossil undo } catch {exec $::fossilexe info} res puts res=$res if {![regexp {use --repository} $res]} { puts stderr "Cannot run this test within an open checkout" return } repo_init # Prepare first commit # write_file f1 "f1" write_file f2 "f2" write_file f3 "f3" fossil add f1 f2 f3 fossil commit -m "c1" # Make changes to be reverted # # Add f0 write_file f0 "f0" fossil add f0 # Remove f1 file delete f1 fossil rm f1 # Edit f2 write_file f2 "f2.1" # Rename f3 to f3n file rename -force f3 f3n fossil mv f3 f3n # Test 'fossil revert' with no arguments # revert-test 1-1 {} { UNMANAGE: f0 REVERTED: f1 REVERTED: f2 REVERTED: f3 DELETE: f3n } -addremove { ADDED f0 } -exists {f0 f1 f2 f3} -notexists f3n # Test with a single filename argument # revert-test 1-2 f0 { UNMANAGE: f0 } -changes { DELETED f1 EDITED f2 RENAMED f3n } -addremove { ADDED f0 } -exists {f0 f2 f3n} -notexists f3 revert-test 1-3 f1 { REVERTED: f1 } -changes { ADDED f0 EDITED f2 RENAMED f3n } -exists {f0 f1 f2 f3n} -notexists f3 revert-test 1-4 f2 { REVERTED: f2 } -changes { ADDED f0 DELETED f1 RENAMED f3n } -exists {f0 f2 f3n} -notexists {f1 f3} # Both files involved in a rename are reverted regardless of which filename # is used as an argument to 'fossil revert' # revert-test 1-5 f3 { REVERTED: f3 DELETE: f3n } -changes { ADDED f0 DELETED f1 EDITED f2 } -exists {f0 f2 f3} -notexists {f1 f3n} revert-test 1-6 f3n { REVERTED: f3 DELETE: f3n } -changes { ADDED f0 DELETED f1 EDITED f2 } -exists {f0 f2 f3} -notexists {f1 f3n} # Test with multiple filename arguments # revert-test 1-7 {f0 f2 f3n} { UNMANAGE: f0 REVERTED: f2 REVERTED: f3 DELETE: f3n } -changes { DELETED f1 } -addremove { ADDED f0 } -exists {f0 f2 f3} -notexists {f1 f3n} # Test reverting the combination of a renamed file and an added file that # uses the renamed file's original filename. # repo_init write_file f1 "f1" fossil add f1 fossil commit -m "add f1" write_file f1n "f1n" fossil mv f1 f1n write_file f1 "f1b" fossil add f1 revert-test 2-1 {} { REVERTED: f1 DELETE: f1n } -exists {f1} -notexists {f1n} |
Added test/subdir-b/readme.txt.
> > > | 1 2 3 | This file exists in order to create the "subdir-b" subdirectory. There is exists sibling directory "subdir" that is a prefix of this subdirectory. This file exists for self-testing. |
Added test/subdir/one/two/three/four/five/six/readme.txt.
> > | 1 2 | This file exists in order to provide Fossil with a test case of a file that is deeply nested below many subdirectories. |
Added test/test-page++.wiki.
> > > > > > > | 1 2 3 4 5 6 7 | <title>Test Page</title> The purpose of this page is to test Fossil's ability to deal with embedded documentation pages that contain characters that should be escaped in URLs. Here is a link to the [./release-checklist.wiki | release checklist]. |
Changes to test/tester.tcl.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | # # tclsh ../test/tester.tcl ../bld/fossil # # Where ../test/tester.tcl is the name of this file and ../bld/fossil # is the name of the executable to be tested. # set testdir [file normalize [file dir $argv0]] set fossilexe [file normalize [lindex $argv 0]] set argv [lrange $argv 1 end] set i [lsearch $argv -halt] if {$i>=0} { set HALT 1 set argv [lreplace $argv $i $i] } else { set HALT 0 } set i [lsearch $argv -prot] if {$i>=0} { set PROT 1 set argv [lreplace $argv $i $i] } else { set PROT 0 } if {[llength $argv]==0} { foreach f [lsort [glob $testdir/*.test]] { set base [file root [file tail $f]] lappend argv $base } } # start protocol # proc protInit {cmd} { if {$::PROT} { | > > > > > > > > > > > > > > > > | | | | | | > > > > | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | # # tclsh ../test/tester.tcl ../bld/fossil # # Where ../test/tester.tcl is the name of this file and ../bld/fossil # is the name of the executable to be tested. # set testrundir [pwd] set testdir [file normalize [file dir $argv0]] set fossilexe [file normalize [lindex $argv 0]] set argv [lrange $argv 1 end] set i [lsearch $argv -halt] if {$i>=0} { set HALT 1 set argv [lreplace $argv $i $i] } else { set HALT 0 } set i [lsearch $argv -prot] if {$i>=0} { set PROT 1 set argv [lreplace $argv $i $i] } else { set PROT 0 } set i [lsearch $argv -verbose] if {$i>=0} { set VERBOSE 1 set argv [lreplace $argv $i $i] } else { set VERBOSE 0 } if {[llength $argv]==0} { foreach f [lsort [glob $testdir/*.test]] { set base [file root [file tail $f]] lappend argv $base } } set tempPath [expr {[info exists env(TEMP)] ? \ $env(TEMP) : [file dirname [info script]]}] if {$tcl_platform(platform) eq "windows"} then { set tempPath [string map [list \\ /] $tempPath] } # start protocol # proc protInit {cmd} { if {$::PROT} { set out [open [file join $::testrundir prot] w] fconfigure $out -translation platform puts $out "starting tests with: $cmd" close $out } } # write protocol # proc protOut {msg} { puts stdout $msg if {$::PROT} { set out [open [file join $::testrundir prot] a] fconfigure $out -translation platform puts $out $msg close $out } } # Run the fossil program # proc fossil {args} { global fossilexe set cmd $fossilexe foreach a $args { lappend cmd $a } protOut $cmd flush stdout set rc [catch {eval exec $cmd} result] global RESULT CODE set CODE $rc if {$rc} { protOut "ERROR: $result" } elseif {$::VERBOSE} { protOut "RESULT: $result" } set RESULT $result } # Read a file into memory. # proc read_file {filename} { set in [open $filename r] fconfigure $in -translation binary set txt [read $in [file size $filename]] close $in return $txt |
︙ | ︙ | |||
118 119 120 121 122 123 124 125 126 127 128 129 130 131 | proc same_file {a b} { set x [read_file $a] regsub -all { +\n} $x \n x set y [read_file $b] regsub -all { +\n} $y \n y return [expr {$x==$y}] } # Perform a test # set test_count 0 proc test {name expr} { global bad_test test_count incr test_count | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | proc same_file {a b} { set x [read_file $a] regsub -all { +\n} $x \n x set y [read_file $b] regsub -all { +\n} $y \n y return [expr {$x==$y}] } # Create and open a new Fossil repository and clean the checkout # proc repo_init {{filename ".rep.fossil"}} { if {$::env(HOME) ne [pwd]} { catch {exec $::fossilexe info} res if {![regexp {use --repository} $res]} { error "In an open checkout: cannot initialize a new repository here." } # Fossil will write data on $HOME, running 'fossil new' here. # We need not to clutter the $HOME of the test caller. # set ::env(HOME) [pwd] } catch {exec $::fossilexe close -f} file delete $filename exec $::fossilexe new $filename exec $::fossilexe open $filename exec $::fossilexe clean -f exec $::fossilexe set mtime-changes off } # Normalize file status lists (like those returned by 'fossil changes') # so they can be compared using simple string comparison # proc normalize_status_list {list} { set normalized [list] set matches [regexp -all -inline -line {^\s*([A-Z_]+:?)\x20+(\S.*)$} $list] foreach {_ status file} $matches { lappend normalized [list $status [string trim $file]] } set normalized [lsort -index 1 $normalized] return $normalized } # Perform a test comparing two status lists # proc test_status_list {name result expected} { set expected [normalize_status_list $expected] set result [normalize_status_list $result] if {$result eq $expected} { test $name 1 } else { protOut " Expected:\n [join $expected "\n "]" protOut " Got:\n [join $result "\n "]" test $name 0 } } # Append all arguments into a single value and then returns it. # proc appendArgs {args} { eval append result $args } # Return the name of the versioned settings file containing the TH1 # setup script. # proc getTh1SetupFileName {} { # # NOTE: This uses the "testdir" global variable provided by the # test suite; alternatively, the root of the source tree # could be obtained directly from Fossil. # return [file normalize [file join [file dirname $::testdir] \ .fossil-settings th1-setup]] } # Return the saved name of the versioned settings file containing # the TH1 setup script. # proc getSavedTh1SetupFileName {} { return [appendArgs [getTh1SetupFileName] . [pid]] } # Sets the TH1 setup script to the one provided. Prior to calling # this, the [saveTh1SetupFile] procedure should be called in order to # preserve the existing TH1 setup script. Prior to completing the test, # the [restoreTh1SetupFile] procedure should be called to restore the # original TH1 setup script. # proc writeTh1SetupFile { data } { return [write_file [getTh1SetupFileName] $data] } # Saves the TH1 setup script file by renaming it, based on the current # process ID. # proc saveTh1SetupFile {} { set oldFileName [getTh1SetupFileName] if {[file exists $oldFileName]} then { set newFileName [getSavedTh1SetupFileName] catch {file delete $newFileName} file rename $oldFileName $newFileName } } # Restores the original TH1 setup script file by renaming it back, based # on the current process ID. # proc restoreTh1SetupFile {} { set oldFileName [getSavedTh1SetupFileName] set newFileName [getTh1SetupFileName] if {[file exists $oldFileName]} then { catch {file delete $newFileName} file rename $oldFileName $newFileName } else { # # NOTE: There was no TH1 setup script file, delete the test one. # file delete $newFileName } } # Perform a test # set test_count 0 proc test {name expr} { global bad_test test_count incr test_count |
︙ | ︙ | |||
193 194 195 196 197 198 199 200 201 202 203 204 205 206 | set line [string range $line 0 $i]$stuff[string range $line $ip1 end] } } append out \n$line } return [string range $out 1 end] } protInit $fossilexe foreach testfile $argv { set dir [file root [file tail $testfile]] file delete -force $dir file mkdir $dir set origwd [pwd] | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | set line [string range $line 0 $i]$stuff[string range $line $ip1 end] } } append out \n$line } return [string range $out 1 end] } # Executes the "fossil http" command. The entire content of the HTTP request # is read from the data file name, with [subst] being performed on it prior to # submission. Temporary input and output files are created and deleted. The # result will be the contents of the temoprary output file. proc test_fossil_http { repository dataFileName url } { set suffix [appendArgs [pid] - [getSeqNo] - [clock seconds] .txt] set inFileName [file join $::tempPath [appendArgs test-http-in- $suffix]] set outFileName [file join $::tempPath [appendArgs test-http-out- $suffix]] set data [subst [read_file $dataFileName]] write_file $inFileName $data fossil http $inFileName $outFileName 127.0.0.1 $repository --localauth set result [expr {[file exists $outFileName] ? [read_file $outFileName] : ""}] if {1} then { catch {file delete $inFileName} catch {file delete $outFileName} } return $result } # obtains and increments a "sequence number" for this test run. proc getSeqNo {} { upvar #0 seqNo seqNo if {![info exists seqNo]} { set seqNo 0 } return [incr seqNo] } # fixup the whitespace in the result to make it easier to compare. proc normalize_result {} { return [string map [list \r\n \n] [string trim $::RESULT]] } # returns the first line of the normalized result. proc first_data_line {} { return [lindex [split [normalize_result] \n] 0] } # returns the second line of the normalized result. proc second_data_line {} { return [lindex [split [normalize_result] \n] 1] } # returns the third line of the normalized result. proc third_data_line {} { return [lindex [split [normalize_result] \n] 2] } # returns the last line of the normalized result. proc last_data_line {} { return [lindex [split [normalize_result] \n] end] } # returns the second to last line of the normalized result. proc next_to_last_data_line {} { return [lindex [split [normalize_result] \n] end-1] } # returns the third to last line of the normalized result. proc third_to_last_data_line {} { return [lindex [split [normalize_result] \n] end-2] } protInit $fossilexe foreach testfile $argv { set dir [file root [file tail $testfile]] file delete -force $dir file mkdir $dir set origwd [pwd] |
︙ | ︙ |
Added test/th1-docs-input.txt.
> > > > | 1 2 3 4 | GET ${url} HTTP/1.1 Host: localhost User-Agent: Fossil |
Added test/th1-docs.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | # # Copyright (c) 2015 D. Richard Hipp # # This program is free software; you can redistribute it and/or # modify it under the terms of the Simplified BSD License (also # known as the "2-Clause License" or "FreeBSD License".) # # This program is distributed in the hope that it will be useful, # but without any warranty; without even the implied warranty of # merchantability or fitness for a particular purpose. # # Author contact information: # drh@hwaci.com # http://www.hwaci.com/drh/ # ############################################################################ # # TH1 Docs # fossil test-th-eval "hasfeature th1Docs" if {$::RESULT ne "1"} then { puts "Fossil was not compiled with TH1 docs support."; return } fossil test-th-eval "hasfeature tcl" if {$::RESULT ne "1"} then { puts "Fossil was not compiled with Tcl support."; return } ############################################################################### set env(TH1_ENABLE_DOCS) 1; # TH1 docs must be enabled for this test. set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test. ############################################################################### set data [fossil info] regexp -line -- {^repository: (.*)$} $data dummy repository if {[string length $repository] == 0 || ![file exists $repository]} then { error "unable to locate repository" } set dataFileName [file join $::testdir th1-docs-input.txt] ############################################################################### set RESULT [test_fossil_http \ $repository $dataFileName /doc/trunk/test/fileStat.th1] test th1-docs-1a {[regexp {<title>Fossil: test/fileStat.th1</title>} $RESULT]} test th1-docs-1b {[regexp {>\[[0-9a-f]{40}\]<} $RESULT]} test th1-docs-1c {[regexp { contains \d+ files\.} $RESULT]} |
Added test/th1-hooks-input.txt.
> > > > | 1 2 3 4 | GET ${url} HTTP/1.1 Host: localhost User-Agent: Fossil |
Added test/th1-hooks.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | # # Copyright (c) 2011 D. Richard Hipp # # This program is free software; you can redistribute it and/or # modify it under the terms of the Simplified BSD License (also # known as the "2-Clause License" or "FreeBSD License".) # # This program is distributed in the hope that it will be useful, # but without any warranty; without even the implied warranty of # merchantability or fitness for a particular purpose. # # Author contact information: # drh@hwaci.com # http://www.hwaci.com/drh/ # ############################################################################ # # TH1 Hooks # fossil test-th-eval "hasfeature th1Hooks" if {$::RESULT ne "1"} then { puts "Fossil was not compiled with TH1 hooks support."; return } ############################################################################### set env(TH1_ENABLE_HOOKS) 1; # TH1 hooks must be enabled for this test. ############################################################################### set testTh1Setup { proc initialize_hook_log {} { if {![info exists ::hook_log]} { set ::hook_log "" } } proc append_hook_log { args } { initialize_hook_log if {[string length $::hook_log] > 0} { set ::hook_log "$::hook_log " } for {set i 0} {$i < [llength $args]} {set i [expr {$i + 1}]} { set ::hook_log $::hook_log[lindex $args $i] } } proc emit_hook_log {} { initialize_hook_log html "\n<h1><b>$::hook_log</b></h1>\n" } proc command_hook {} { append_hook_log command_hook " " $::cmd_name if {$::cmd_name eq "test1"} { puts [repository]; continue } elseif {$::cmd_name eq "test2"} { error "unsupported command" } elseif {$::cmd_name eq "test3"} { emit_hook_log break "TH_BREAK return code" } elseif {$::cmd_name eq "test4"} { emit_hook_log return -code 2 "TH_RETURN return code" } elseif {$::cmd_name eq "timeline"} { set length [llength $::cmd_args] set length [expr {$length - 1}] if {[lindex $::cmd_args $length] eq "custom"} { emit_hook_log return "custom timeline" } elseif {[lindex $::cmd_args $length] eq "now"} { emit_hook_log return "now timeline" } else { emit_hook_log error "unsupported timeline" } } } proc command_notify {} { append_hook_log command_notify " " $::cmd_name emit_hook_log } proc webpage_hook {} { append_hook_log webpage_hook " " $::web_name if {$::web_name eq "test1"} { puts [repository]; continue } } proc webpage_notify {} { append_hook_log webpage_notify " " $::web_name emit_hook_log } } ############################################################################### set data [fossil info] regexp -line -- {^repository: (.*)$} $data dummy repository if {[string length $repository] == 0 || ![file exists $repository]} then { error "unable to locate repository" } set dataFileName [file join $::testdir th1-hooks-input.txt] ############################################################################### saveTh1SetupFile; writeTh1SetupFile $testTh1Setup ############################################################################### fossil timeline custom; # NOTE: Bad "WHEN" argument. test th1-cmd-hooks-1a {[normalize_result] eq \ {<h1><b>command_hook timeline</b></h1> +++ no more data (0) +++ <h1><b>command_hook timeline command_notify timeline</b></h1>}} ############################################################################### fossil timeline test th1-cmd-hooks-2a {[first_data_line] eq \ {<h1><b>command_hook timeline</b></h1>}} test th1-cmd-hooks-2b {[second_data_line] eq {ERROR: unsupported timeline}} ############################################################################### fossil timeline now test th1-cmd-hooks-3a {[first_data_line] eq \ {<h1><b>command_hook timeline</b></h1>}} test th1-cmd-hooks-3b \ {[regexp -- {=== \d{4}-\d{2}-\d{2} ===} [second_data_line]]} test th1-cmd-hooks-3c \ {[regexp -- {--- line limit \(\d+\) reached ---} [third_to_last_data_line]]} test th1-cmd-hooks-3d {[last_data_line] eq \ {<h1><b>command_hook timeline command_notify timeline</b></h1>}} ############################################################################### fossil test1 test th1-custom-cmd-1a {[next_to_last_data_line] eq $repository} test th1-custom-cmd-1b {[last_data_line] eq \ {<h1><b>command_hook test1 command_notify test1</b></h1>}} ############################################################################### fossil test2 test th1-custom-cmd-2a {[first_data_line] eq {ERROR: unsupported command}} ############################################################################### fossil test3 test th1-custom-cmd-3a {[string trim $RESULT] eq \ {<h1><b>command_hook test3</b></h1>}} ############################################################################### fossil test4 test th1-custom-cmd-4a {[string trim $RESULT] eq \ {<h1><b>command_hook test4</b></h1>}} ############################################################################### set RESULT [test_fossil_http $repository $dataFileName /timeline] test th1-web-hooks-1a {[regexp {<title>Fossil: Timeline</title>} $RESULT]} test th1-web-hooks-1b {[regexp [appendArgs \ {<h1><b>command_hook http webpage_hook timeline} " " \ {webpage_notify timeline</b></h1>}] $RESULT]} ############################################################################### set RESULT [test_fossil_http $repository $dataFileName /test1] test th1-custom-web-1a {[next_to_last_data_line] eq $repository} test th1-custom-web-1b {[last_data_line] eq \ {<h1><b>command_hook http webpage_hook test1 webpage_notify test1</b></h1>}} ############################################################################### restoreTh1SetupFile |
Changes to test/th1-tcl.test.
︙ | ︙ | |||
30 31 32 33 34 35 36 | ############################################################################### set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test. ############################################################################### | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | ############################################################################### set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test. ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $dir th1-tcl1.txt]] test th1-tcl-1 {[regexp -- {^tclReady\(before\) = 0 tclReady\(after\) = 1 \d+ \d+ \d+ |
︙ | ︙ | |||
57 58 59 60 61 62 63 | \d+ one_word three words now $} [string map [list \r\n \n] $RESULT]]} ############################################################################### | | | | | | | | | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | \d+ one_word three words now $} [string map [list \r\n \n] $RESULT]]} ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $dir th1-tcl2.txt]] test th1-tcl-2 {[regexp -- {^\d+ $} [string map [list \r\n \n] $RESULT]]} ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $dir th1-tcl3.txt]] test th1-tcl-3 {$RESULT eq {<hr><p class="thmainError">ERROR:\ invalid command name "bad_command"</p>}} ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $dir th1-tcl4.txt]] test th1-tcl-4 {$RESULT eq {<hr><p class="thmainError">ERROR:\ divide by zero</p>}} ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $dir th1-tcl5.txt]] test th1-tcl-5 {$RESULT eq {<hr><p class="thmainError">ERROR:\ Tcl command not found: bad_command</p>} || $RESULT eq {<hr><p\ class="thmainError">ERROR: invalid command name "bad_command"</p>}} ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $dir th1-tcl6.txt]] test th1-tcl-6 {$RESULT eq {<hr><p class="thmainError">ERROR:\ no such command: bad_command</p>}} ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $dir th1-tcl7.txt]] test th1-tcl-7 {$RESULT eq {<hr><p class="thmainError">ERROR:\ syntax error in expression: "2**0"</p>}} ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $dir th1-tcl8.txt]] test th1-tcl-8 {$RESULT eq {<hr><p class="thmainError">ERROR:\ cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr><p\ class="thmainError">ERROR: tailcall can only be called from a proc or\ lambda</p>} || $RESULT eq {<hr><p class="thmainError">ERROR: This test\ requires Tcl 8.6 or higher.</p>}} ############################################################################### fossil test-th-render --open-config \ [file nativename [file join $dir th1-tcl9.txt]] test th1-tcl-9 {[string trim $RESULT] eq [list [file tail $fossilexe] 3 \ [list test-th-render --open-config [file nativename [file join $dir \ th1-tcl9.txt]]]]} |
Changes to test/th1.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 | # http://www.hwaci.com/drh/ # ############################################################################ # # TH1 Commands # | > > > > > | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 | # http://www.hwaci.com/drh/ # ############################################################################ # # TH1 Commands # fossil test-th-eval --open-config "setting th1-hooks" set th1Hooks [expr {$RESULT eq "1"}] ############################################################################### fossil test-th-eval --open-config "setting abc" test th1-setting-1 {$RESULT eq ""} ############################################################################### fossil test-th-eval --open-config "setting -- abc" test th1-setting-2 {$RESULT eq ""} ############################################################################### fossil test-th-eval --open-config "setting -strict abc" test th1-setting-3 {$RESULT eq {TH_ERROR: no value for setting "abc"}} ############################################################################### fossil test-th-eval --open-config "setting -strict -- abc" test th1-setting-4 {$RESULT eq {TH_ERROR: no value for setting "abc"}} ############################################################################### fossil test-th-eval --open-config "setting autosync" test th1-setting-5 {$RESULT eq 0 || $RESULT eq 1} ############################################################################### fossil test-th-eval --open-config "setting -strict autosync" test th1-setting-6 {$RESULT eq 0 || $RESULT eq 1} ############################################################################### fossil test-th-eval --open-config "setting --" test th1-setting-7 {$RESULT eq \ {TH_ERROR: wrong # args: should be "setting ?-strict? ?--? name"}} ############################################################################### fossil test-th-eval --open-config "setting -strict --" test th1-setting-8 {$RESULT eq \ {TH_ERROR: wrong # args: should be "setting ?-strict? ?--? name"}} ############################################################################### fossil test-th-eval --open-config "setting -- --" test th1-setting-9 {$RESULT eq {}} ############################################################################### fossil test-th-eval --open-config "setting -strict -- --" test th1-setting-10 {$RESULT eq {TH_ERROR: no value for setting "--"}} ############################################################################### fossil test-th-eval "expr 42/0" test th1-divide-by-zero-1 {$RESULT eq {TH_ERROR: Divide by 0: 42}} ############################################################################### fossil test-th-eval "expr 42/0.0" test th1-divide-by-zero-2 {$RESULT eq {TH_ERROR: Divide by 0: 42}} ############################################################################### fossil test-th-eval "expr 42.0/0" test th1-divide-by-zero-3 {$RESULT eq {TH_ERROR: Divide by 0: 42.0}} ############################################################################### fossil test-th-eval "expr 42.0/0.0" test th1-divide-by-zero-4 {$RESULT eq {TH_ERROR: Divide by 0: 42.0}} ############################################################################### fossil test-th-eval "expr 42%0" test th1-modulus-by-zero-1 {$RESULT eq {TH_ERROR: Modulo by 0: 42}} ############################################################################### fossil test-th-eval "expr 42%0.0" test th1-modulus-by-zero-2 {$RESULT eq {TH_ERROR: expected integer, got: "0.0"}} ############################################################################### fossil test-th-eval "expr 42.0%0" test th1-modulus-by-zero-3 {$RESULT eq \ {TH_ERROR: expected integer, got: "42.0"}} ############################################################################### fossil test-th-eval "expr 42.0%0.0" test th1-modulus-by-zero-4 {$RESULT eq \ {TH_ERROR: expected integer, got: "42.0"}} ############################################################################### fossil test-th-eval "set var 1; info exists var" test th1-info-exists-1 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "set var 1; unset var; info exists var" test th1-info-exists-2 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "set var 1; unset var; set var 2; info exists var" test th1-info-exists-3 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "set var 1; expr {\$var+0}" test th1-info-exists-4 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "set var 1; unset var; expr {\$var+0}" test th1-info-exists-5 {$RESULT eq {TH_ERROR: no such variable: var}} ############################################################################### fossil test-th-eval "catch {bad}; info exists var; set th_stack_trace" test th1-info-exists-6 {$RESULT eq {bad}} ############################################################################### fossil test-th-eval "set var(1) 1; info exists var" test th1-info-exists-7 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "set var(1) 1; unset var(1); info exists var" test th1-info-exists-8 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "set var(1) 1; unset var; info exists var" test th1-info-exists-9 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "set var(1) 1; info exists var(1)" test th1-info-exists-10 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "set var(1) 1; unset var(1); info exists var(1)" test th1-info-exists-11 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "set var(1) 1; unset var; info exists var(1)" test th1-info-exists-12 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "set var 1; unset var" test th1-unset-1 {$RESULT eq {var}} ############################################################################### fossil test-th-eval "unset var" test th1-unset-2 {$RESULT eq {TH_ERROR: no such variable: var}} ############################################################################### fossil test-th-eval "set var 1; unset var; unset var" test th1-unset-3 {$RESULT eq {TH_ERROR: no such variable: var}} ############################################################################### fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv" test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}} ############################################################################### fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2" test th1-unset-5 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2" test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}} ############################################################################### fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)" test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}} ############################################################################### fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2" test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}} ############################################################################### fossil test-th-eval "string first {} {}" test th1-string-first-1 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "string first {} {a}" test th1-string-first-2 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "string first {a} {}" test th1-string-first-3 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "string first {a} {a}" test th1-string-first-4 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "string first {a} {aa}" test th1-string-first-5 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "string first {aa} {a}" test th1-string-first-6 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "string first {ab} {abc}" test th1-string-first-7 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "string first {bc} {abc}" test th1-string-first-8 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "string first {AB} {abc}" test th1-string-first-9 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "string last {} {}" test th1-string-last-1 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "string last {} {a}" test th1-string-last-2 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "string last {a} {}" test th1-string-last-3 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "string last {a} {a}" test th1-string-last-4 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "string last {a} {aa}" test th1-string-last-5 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "string last {aa} {a}" test th1-string-last-6 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "string last {ab} {abc}" test th1-string-last-7 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "string last {bc} {abc}" test th1-string-last-8 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "string last {AB} {abc}" test th1-string-last-9 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "expr -2147483649.0" test th1-expr-1 {$RESULT eq {-2147483649.0}} ############################################################################### fossil test-th-eval "expr -2147483649" test th1-expr-2 {$RESULT eq {2147483647}} ############################################################################### fossil test-th-eval "expr -2147483648" test th1-expr-3 {$RESULT eq {-2147483648}} ############################################################################### fossil test-th-eval "expr -2147483647" test th1-expr-4 {$RESULT eq {-2147483647}} ############################################################################### fossil test-th-eval "expr -1" test th1-expr-5 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "expr 0" test th1-expr-6 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "expr 0.0" test th1-expr-7 {$RESULT eq {0.0}} ############################################################################### fossil test-th-eval "expr 1" test th1-expr-8 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "expr 2147483647" test th1-expr-9 {$RESULT eq {2147483647}} ############################################################################### fossil test-th-eval "expr 2147483648" test th1-expr-10 {$RESULT eq {2147483648}} ############################################################################### fossil test-th-eval "expr 2147483649" test th1-expr-11 {$RESULT eq {2147483649}} ############################################################################### fossil test-th-eval "expr +2147483649" test th1-expr-12 {$RESULT eq {-2147483647}} ############################################################################### fossil test-th-eval "expr +2147483649.0" test th1-expr-13 {$RESULT eq {2147483649.0}} ############################################################################### fossil test-th-eval "expr ~(-1)" test th1-expr-14 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "expr ~-1" test th1-expr-15 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "expr ~0" test th1-expr-16 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "expr ~+0" test th1-expr-17 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "expr ~-0" test th1-expr-18 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "expr ~(+0)" test th1-expr-19 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "expr ~(-0)" test th1-expr-20 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "expr ~1" test th1-expr-21 {$RESULT eq {-2}} ############################################################################### fossil test-th-eval "expr ~+1" test th1-expr-22 {$RESULT eq {-2}} ############################################################################### fossil test-th-eval "expr ~(+1)" test th1-expr-23 {$RESULT eq {-2}} ############################################################################### fossil test-th-eval "expr 0+0b11" test th1-expr-24 {$RESULT eq 3} ############################################################################### fossil test-th-eval "expr 0+0o15" test th1-expr-25 {$RESULT eq 13} ############################################################################### fossil test-th-eval "expr 0+0x15" test th1-expr-26 {$RESULT eq 21} ############################################################################### fossil test-th-eval "expr 0+0b2" test th1-expr-27 {$RESULT eq {TH_ERROR: expected number, got: "0b2"}} ############################################################################### fossil test-th-eval "expr 0+0o8" test th1-expr-28 {$RESULT eq {TH_ERROR: expected number, got: "0o8"}} ############################################################################### fossil test-th-eval "expr 0+0xg" test th1-expr-29 {$RESULT eq {TH_ERROR: syntax error in expression: "0+0xg"}} ############################################################################### fossil test-th-eval "expr 0+0b1." test th1-expr-30 {$RESULT eq {TH_ERROR: syntax error in expression: "0+0b1."}} ############################################################################### fossil test-th-eval "expr 0+0o1." test th1-expr-31 {$RESULT eq {TH_ERROR: syntax error in expression: "0+0o1."}} ############################################################################### fossil test-th-eval "expr 0+0x1." test th1-expr-32 {$RESULT eq {TH_ERROR: syntax error in expression: "0+0x1."}} ############################################################################### fossil test-th-eval "expr 0ne5" test th1-expr-33 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "expr 0b1+5" test th1-expr-34 {$RESULT eq {6}} ############################################################################### fossil test-th-eval "expr 0+0b" test th1-expr-35 {$RESULT eq {TH_ERROR: expected number, got: "0b"}} ############################################################################### fossil test-th-eval "expr (-1)+1" test th1-expr-36 {$RESULT eq {0}} ############################################################################### fossil test-th-eval "expr (((-1)))" test th1-expr-37 {$RESULT eq {-1}} ############################################################################### fossil test-th-eval "expr (((1)))" test th1-expr-38 {$RESULT eq {1}} ############################################################################### fossil test-th-eval "expr (((1))" test th1-expr-39 {$RESULT eq {TH_ERROR: syntax error in expression: "(((1))"}} ############################################################################### fossil test-th-eval "expr ((1)))" test th1-expr-40 {$RESULT eq {TH_ERROR: syntax error in expression: "((1)))"}} ############################################################################### fossil test-th-eval "expr (((1)*2)*2)" test th1-expr-41 {$RESULT eq {4}} ############################################################################### fossil test-th-eval "expr +" test th1-expr-42 {$RESULT eq {TH_ERROR: syntax error in expression: "+"}} ############################################################################### fossil test-th-eval "expr -" test th1-expr-43 {$RESULT eq {TH_ERROR: syntax error in expression: "-"}} ############################################################################### fossil test-th-eval "expr ++" test th1-expr-44 {$RESULT eq {TH_ERROR: syntax error in expression: "++"}} ############################################################################### fossil test-th-eval "expr --" test th1-expr-45 {$RESULT eq {TH_ERROR: syntax error in expression: "--"}} ############################################################################### fossil test-th-eval "lindex list +" test th1-expr-46 {$RESULT eq {TH_ERROR: expected integer, got: "+"}} ############################################################################### fossil test-th-eval "lindex list -" test th1-expr-47 {$RESULT eq {TH_ERROR: expected integer, got: "-"}} ############################################################################### fossil test-th-eval "lindex list +0x" test th1-expr-48 {$RESULT eq {TH_ERROR: expected integer, got: "+0x"}} ############################################################################### fossil test-th-eval "lindex list -0x" test th1-expr-49 {$RESULT eq {TH_ERROR: expected integer, got: "-0x"}} ############################################################################### fossil test-th-eval "checkout 1"; # NOTE: Assumes running "in tree". test th1-checkout-1 {[string length $RESULT] > 0} ############################################################################### fossil test-th-eval "checkout"; # NOTE: Assumes running "in tree". test th1-checkout-2 {[string length $RESULT] > 0} ############################################################################### set savedPwd [pwd]; cd / fossil test-th-eval "checkout 1" cd $savedPwd; unset savedPwd test th1-checkout-3 {[string length $RESULT] == 0} ############################################################################### set savedPwd [pwd]; cd / fossil test-th-eval "checkout" cd $savedPwd; unset savedPwd test th1-checkout-4 {[string length $RESULT] == 0} ############################################################################### fossil test-th-eval "render {}" test th1-render-1 {$RESULT eq {}} ############################################################################### fossil test-th-eval "render {$<x> before <th1>set x 123</th1> after $<x> }" test th1-render-2 {$RESULT eq {no such variable: x before after 123 }} ############################################################################### fossil test-th-eval "trace {}" test th1-trace-1 {$RESULT eq {}} ############################################################################### fossil test-th-eval --th-trace "trace {}" if {$th1Hooks} { test th1-trace-2 {[string map [list \r\n \n] [string trim $RESULT]] eq \ {------------------ BEGIN TRACE LOG ------------------ th1-init 0x0 => 0x0<br /> ------------------- END TRACE LOG -------------------}} } else { test th1-trace-2 {[string map [list \r\n \n] [string trim $RESULT]] eq \ {------------------ BEGIN TRACE LOG ------------------ th1-init 0x0 => 0x0<br /> th1-setup {} => TH_OK<br /> ------------------- END TRACE LOG -------------------}} } ############################################################################### fossil test-th-eval "trace {this is a trace message.}" test th1-trace-3 {$RESULT eq {}} ############################################################################### fossil test-th-eval --th-trace "trace {this is a trace message.}" if {$th1Hooks} { test th1-trace-4 {[string map [list \r\n \n] [string trim $RESULT]] eq \ {------------------ BEGIN TRACE LOG ------------------ th1-init 0x0 => 0x0<br /> this is a trace message. ------------------- END TRACE LOG -------------------}} } else { test th1-trace-4 {[string map [list \r\n \n] [string trim $RESULT]] eq \ {------------------ BEGIN TRACE LOG ------------------ th1-init 0x0 => 0x0<br /> th1-setup {} => TH_OK<br /> this is a trace message. ------------------- END TRACE LOG -------------------}} } ############################################################################### fossil test-th-eval "styleHeader {Page Title Here}" test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}} ############################################################################### fossil test-th-eval --open-config "styleHeader {Page Title Here}" test th1-header-2 {[regexp -- {<title>Fossil: Page Title Here</title>} $RESULT]} ############################################################################### fossil test-th-eval "styleFooter" test th1-footer-1 {$RESULT eq {TH_ERROR: repository unavailable}} ############################################################################### fossil test-th-eval --open-config "styleFooter" test th1-footer-2 {$RESULT eq {}} ############################################################################### fossil test-th-eval --open-config --cgi "styleHeader {}; styleFooter" test th1-footer-3 {[regexp -- {</body></html>} $RESULT]} ############################################################################### fossil test-th-eval "getParameter" test th1-get-parameter-1 {$RESULT eq \ {TH_ERROR: wrong # args: should be "getParameter NAME ?DEFAULT?"}} ############################################################################### fossil test-th-eval "getParameter test1" test th1-get-parameter-2 {$RESULT eq {}} ############################################################################### fossil test-th-eval "getParameter test1 defValue1" test th1-get-parameter-3 {$RESULT eq {defValue1}} ############################################################################### fossil test-th-eval "setParameter" test th1-set-parameter-1 {$RESULT eq \ {TH_ERROR: wrong # args: should be "setParameter NAME VALUE"}} ############################################################################### fossil test-th-eval "setParameter test1 value1; getParameter test1" test th1-set-parameter-2 {$RESULT eq {value1}} ############################################################################### fossil test-th-eval "setParameter test2 value2; getParameter test1" test th1-set-parameter-3 {$RESULT eq {}} ############################################################################### fossil test-th-eval "setParameter test3 value3; getParameter test3" test th1-set-parameter-4 {$RESULT eq {value3}} ############################################################################### fossil test-th-eval "setParameter test3 value3; getParameter test3 defValue3" test th1-set-parameter-5 {$RESULT eq {value3}} ############################################################################### fossil test-th-eval "setParameter test4 value4; setParameter test4 value5; getParameter test4" test th1-set-parameter-6 {$RESULT eq {value5}} ############################################################################### fossil test-th-eval "setParameter test4 value4; setParameter test4 value5; getParameter test4 defValue4" test th1-set-parameter-7 {$RESULT eq {value5}} ############################################################################### fossil test-th-eval "artifact" test th1-artifact-1 {$RESULT eq \ {TH_ERROR: wrong # args: should be "artifact ID ?FILENAME?"}} ############################################################################### fossil test-th-eval "artifact tip" test th1-artifact-2 {$RESULT eq {TH_ERROR: repository unavailable}} ############################################################################### fossil test-th-eval --open-config "artifact tip" test th1-artifact-3 {[regexp -- {F test/th1\.test [0-9a-f]{40}} $RESULT]} ############################################################################### fossil test-th-eval "artifact 0000000000" test th1-artifact-4 {$RESULT eq {TH_ERROR: repository unavailable}} ############################################################################### fossil test-th-eval --open-config "artifact 0000000000" test th1-artifact-5 {$RESULT eq {TH_ERROR: name not found}} ############################################################################### fossil test-th-eval "artifact tip test/th1.test" test th1-artifact-6 {$RESULT eq {TH_ERROR: repository unavailable}} ############################################################################### fossil test-th-eval --open-config "artifact tip test/th1.test" test th1-artifact-7 {[regexp -- {th1-artifact-7} $RESULT]} ############################################################################### fossil test-th-eval "artifact 0000000000 test/th1.test" test th1-artifact-8 {$RESULT eq {TH_ERROR: repository unavailable}} ############################################################################### fossil test-th-eval --open-config "artifact 0000000000 test/th1.test" test th1-artifact-9 {$RESULT eq {TH_ERROR: manifest not found}} ############################################################################### fossil test-th-eval "globalState checkout" test th1-globalState-1 {[string length $RESULT] > 0} ############################################################################### fossil test-th-eval "globalState checkout" test th1-globalState-2 {$RESULT eq [fossil test-th-eval checkout]} ############################################################################### fossil test-th-eval "globalState configuration" test th1-globalState-3 {[string length $RESULT] == 0} ############################################################################### fossil test-th-eval --open-config "globalState configuration" test th1-globalState-4 {[string length $RESULT] > 0} ############################################################################### fossil test-th-eval "globalState executable" test th1-globalState-5 {[file rootname [file tail $RESULT]] eq "fossil"} ############################################################################### fossil test-th-eval "globalState log" test th1-globalState-6 {[string length $RESULT] == 0} ############################################################################### fossil test-th-eval --errorlog foserrors.log "globalState log" test th1-globalState-7 {$RESULT eq "foserrors.log"} ############################################################################### fossil test-th-eval "globalState repository" test th1-globalState-8 {[string length $RESULT] > 0} ############################################################################### fossil test-th-eval "globalState repository" test th1-globalState-9 {$RESULT eq [fossil test-th-eval repository]} ############################################################################### fossil test-th-eval "globalState top" test th1-globalState-10 {[string length $RESULT] == 0} ############################################################################### fossil test-th-eval "globalState user" test th1-globalState-11 {[string length $RESULT] == 0} ############################################################################### fossil test-th-eval --user fossil-th1-test "globalState user" test th1-globalState-12 {$RESULT eq "fossil-th1-test"} ############################################################################### fossil test-th-eval "globalState vfs" test th1-globalState-13 {[string length $RESULT] == 0} ############################################################################### fossil test-th-eval "globalState vfs" test th1-globalState-14 {[string length $RESULT] == 0} ############################################################################### if {$tcl_platform(platform) eq "windows"} then { set altVfs win32-longpath } else { set altVfs unix-dotfile } ############################################################################### fossil test-th-eval --vfs $altVfs "globalState vfs" test th1-globalState-15 {$RESULT eq $altVfs} ############################################################################### fossil test-th-eval "globalState flags" test th1-globalState-16 {$RESULT eq "0"} ############################################################################### fossil test-th-eval "reinitialize; globalState configuration" test th1-reinitialize-1 {$RESULT eq ""} ############################################################################### fossil test-th-eval "reinitialize 1; globalState configuration" test th1-reinitialize-2 {$RESULT ne ""} |
Changes to test/utf.test.
more than 10,000 changes
Added test/utf16be.txt.
> > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | This file contains utf-16be text. The purpose for including this file in the Fossil repository is to provide the ability to test Fossil's handling of UTF-16 using its own repository. Browsing to this file in the web interface should display the file as text on the screen. When there are changes to this file those changes should show up in the "diff" output and be properly displayed on the screen. Test procedures: 1. Verify that this file is correctly display using the /artifact webpage. 2. Verify that this file is correctly displayed by the /doc webpage. 3. Verify that changes to are correctly displayed by the /fdiff webpage. 4. Verify that the "fossil diff" command correctly displays changes in this file. Do the same with the --tk option. |
Added test/utf16le.txt.
> > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | This file contains utf-16le text. The purpose for including this file in the Fossil repository is to provide the ability to test Fossil's handling of UTF-16 using its own repository. Browsing to this file in the web interface should display the file as text on the screen. When there are changes to this file those changes should show up in the "diff" output and be properly displayed on the screen. Test procedures: 1. Verify that this file is correctly display using the /artifact webpage. 2. Verify that this file is correctly displayed by the /doc webpage. 3. Verify that changes to are correctly displayed by the /fdiff webpage. 4. Verify that the "fossil diff" command correctly displays changes in this file. Do the same with the --tk option. |
Changes to test/valgrind-www.tcl.
1 2 | #!/usr/bin/tclsh # | | | > | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #!/usr/bin/tclsh # # Run this script in an open Fossil checkout at the top-level with a # fresh build of Fossil itself. This script will run fossil on hundreds # of different web-pages looking for memory allocation problems using # valgrind. Valgrind output appears on stderr. Suggested test scenario: # # make # tclsh valgrind-www.tcl 2>&1 | tee valgrind-out.txt # # Then examine the valgrind-out.txt file for issues. # proc run_query {url} { set fd [open q.txt w] puts $fd "GET $url HTTP/1.0\r\n\r" close $fd set msg {} catch {exec valgrind ./fossil test-http <q.txt 2>@ stderr} msg return $msg } set todo {} foreach url { /home /timeline /brlist /taglist |
︙ | ︙ |
Changes to tools/fossil_chat.tcl.
︙ | ︙ | |||
82 83 84 85 86 87 88 | proc connect {} { global SOCKET tcl_platform catch {close $SOCKET} if {[catch { if {$::PROXYHOST ne {}} { set SOCKET [socket $::PROXYHOST $::PROXYPORT] puts $SOCKET "CONNECT $::SERVERHOST:$::SERVERPORT HTTP/1.1" | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | proc connect {} { global SOCKET tcl_platform catch {close $SOCKET} if {[catch { if {$::PROXYHOST ne {}} { set SOCKET [socket $::PROXYHOST $::PROXYPORT] puts $SOCKET "CONNECT $::SERVERHOST:$::SERVERPORT HTTP/1.1" puts $SOCKET "Host: $::SERVERHOST:$::SERVERPORT" puts $SOCKET "" } else { set SOCKET [socket $::SERVERHOST $::SERVERPORT] } fconfigure $SOCKET -translation binary -blocking 0 puts $SOCKET [list login $tcl_platform(user) fact,fuzz] flush $SOCKET |
︙ | ︙ | |||
140 141 142 143 144 145 146 | # Delete all of the downloaded files we are currently holding. # proc delete_files {} { global FILES .mb.files delete 3 end array unset FILES | | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | # Delete all of the downloaded files we are currently holding. # proc delete_files {} { global FILES .mb.files delete 3 end array unset FILES .mb.files entryconfigure 1 -state disabled } # Prompt the user to select a file from the disk. Then send that # file to all chat participants. # proc send_file {} { global SOCKET |
︙ | ︙ | |||
186 187 188 189 190 191 192 | if {$filename==$prior} break } if {![info exists prior] || $filename!=$prior} { .mb.files add command -label "Save \"$filename\"" \ -command [list save_file $filename] } set FILES($filename) $data | | | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | if {$filename==$prior} break } if {![info exists prior] || $filename!=$prior} { .mb.files add command -label "Save \"$filename\"" \ -command [list save_file $filename] } set FILES($filename) $data .mb.files entryconfigure 1 -state active set time [clock format [clock seconds] -format {%H:%M} -gmt 1] .msg.t insert end "\[$time $from\] " meta "File: \"$filename\"\n" norm .msg.t see end } # Handle input from the server # |
︙ | ︙ | |||
230 231 232 233 234 235 236 | .msg.t insert end "\[$time\] [lindex $line 1]\n" meta .msg.t see end } elseif {$cmd=="file"} { if {[info commands handle_file]=="handle_file"} { handle_file [lindex $line 1] [lindex $line 2] [lindex $line 3] } } | | | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | .msg.t insert end "\[$time\] [lindex $line 1]\n" meta .msg.t see end } elseif {$cmd=="file"} { if {[info commands handle_file]=="handle_file"} { handle_file [lindex $line 1] [lindex $line 2] [lindex $line 3] } } } # Handle a broken socket connection # proc disconnect {} { global SOCKET close $SOCKET set q [tk_messageBox -icon error -type yesno -parent . -message \ |
︙ | ︙ |
Changes to win/Makefile.PellesCGMake.
︙ | ︙ | |||
28 29 30 31 32 33 34 | # zlib sources 1.2.5 # Windows XP SP 2 # and # PellesC 6.00.4 # gmake 3.80 # zlib sources 1.2.5 # Windows 7 Home Premium | | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | # zlib sources 1.2.5 # Windows XP SP 2 # and # PellesC 6.00.4 # gmake 3.80 # zlib sources 1.2.5 # Windows 7 Home Premium # # PellesCDir=c:\Programme\PellesC # Select between 32/64 bit code, default is 32 bit #TARGETVERSION=64 ifeq ($(TARGETVERSION),64) # 64 bit version |
︙ | ︙ | |||
73 74 75 76 77 78 79 | # define commands for building the windows resource files RESOURCE=fossil.res RC=$(PellesCDir)\bin\porc.exe RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION) # define the special utilities files, needed to generate # the automatically generated source files | | | | | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | # define commands for building the windows resource files RESOURCE=fossil.res RC=$(PellesCDir)\bin\porc.exe RCFLAGS=$(INCLUDE) -D__POCC__=1 -D_M_X$(TARGETVERSION) # define the special utilities files, needed to generate # the automatically generated source files UTILS=translate.exe mkindex.exe makeheaders.exe mkbuiltin.exe UTILS_OBJ=$(UTILS:.exe=.obj) UTILS_SRC=$(foreach uf,$(UTILS),$(SRCDIR)$(uf:.exe=.c)) # define the SQLite files, which need special flags on compile SQLITESRC=sqlite3.c ORIGSQLITESRC=$(foreach sf,$(SQLITESRC),$(SRCDIR)$(sf)) SQLITEOBJ=$(foreach sf,$(SQLITESRC),$(sf:.c=.obj)) SQLITEDEFINES=-DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_WIN32_NO_ANSI # define the SQLite shell files, which need special flags on compile SQLITESHELLSRC=shell.c ORIGSQLITESHELLSRC=$(foreach sf,$(SQLITESHELLSRC),$(SRCDIR)$(sf)) SQLITESHELLOBJ=$(foreach sf,$(SQLITESHELLSRC),$(sf:.c=.obj)) SQLITESHELLDEFINES=-Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen # define the th scripting files, which need special flags on compile THSRC=th.c th_lang.c ORIGTHSRC=$(foreach sf,$(THSRC),$(SRCDIR)$(sf)) THOBJ=$(foreach sf,$(THSRC),$(sf:.c=.obj)) # define the zlib files, needed by this compile |
︙ | ︙ | |||
112 113 114 115 116 117 118 | TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj) # main target file is the application APPLICATION=fossil.exe # define the standard make target .PHONY: default | | | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | TRANSLATEDOBJ=$(TRANSLATEDSRC:.c=.obj) # main target file is the application APPLICATION=fossil.exe # define the standard make target .PHONY: default default: page_index.h builtin_data.h headers $(APPLICATION) # symbolic target to generate the source generate utils .PHONY: utils utils: $(UTILS) # link utils $(UTILS) version.exe: %.exe: %.obj |
︙ | ︙ | |||
138 139 140 141 142 143 144 145 146 | $(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe translate.exe $< >$@ # generate the index source, containing all web references,.. page_index.h: $(TRANSLATEDSRC) mkindex.exe mkindex.exe $(TRANSLATEDSRC) >$@ # extracting version info from manifest VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION | > > > | | | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | $(TRANSLATEDSRC): %_.c: $(SRCDIR)%.c translate.exe translate.exe $< >$@ # generate the index source, containing all web references,.. page_index.h: $(TRANSLATEDSRC) mkindex.exe mkindex.exe $(TRANSLATEDSRC) >$@ builtin_data.h: $(EXTRA_FILES) mkbuiltin.exe mkbuiltin.exe --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ # extracting version info from manifest VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION version.exe ..\manifest.uuid ..\manifest ..\VERSION >$@ # generate the simplified headers headers: makeheaders.exe page_index.h builtin_data.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h echo Done >$@ # compile C sources with relevant options $(TRANSLATEDOBJ): %_.obj: %_.c %.h $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" |
︙ | ︙ |
Changes to win/Makefile.dmc.
︙ | ︙ | |||
22 23 24 25 26 27 28 | SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 | > | > | | | | > | > > > | > > > | | | | > > > | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS3_PARENTHESIS SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E codecheck1$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) codecheck1$E $(SRC) $(DMDIR)\bin\link @link $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo foci fusefs glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR)\translate.c $(BCC) -o$@ $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** mkbuiltin$E: $(SRCDIR)\mkbuiltin.c $(BCC) -o$@ $** mkversion$E: $(SRCDIR)\mkversion.c $(BCC) -o$@ $** codecheck1$E: $(SRCDIR)\codecheck1.c $(BCC) -o$@ $** $(OBJDIR)\shell$O : $(SRCDIR)\shell.c $(TCC) -o$@ -c $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) -o$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $** $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) -o$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) -o$@ -c $** $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h cp $@ $@ VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION +$** > $@ page_index.h: mkindex$E $(SRC) +$** > $@ builtin_data.h: mkbuiltin$E $(EXTRA_FILES) mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ clean: -del $(OBJDIR)\*.obj -del *.obj *_.c *.h *.map realclean: -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E codecheck1$E mkbuiltin$E $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h |
︙ | ︙ | |||
156 157 158 159 160 161 162 163 164 165 166 167 168 169 | +translate$E $** > $@ $(OBJDIR)\browse$O : browse_.c browse.h $(TCC) -o$@ -c browse_.c browse_.c : $(SRCDIR)\browse.c +translate$E $** > $@ $(OBJDIR)\captcha$O : captcha_.c captcha.h $(TCC) -o$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c +translate$E $** > $@ | > > > > > > > > > > > > > > > > > > | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | +translate$E $** > $@ $(OBJDIR)\browse$O : browse_.c browse.h $(TCC) -o$@ -c browse_.c browse_.c : $(SRCDIR)\browse.c +translate$E $** > $@ $(OBJDIR)\builtin$O : builtin_.c builtin.h $(TCC) -o$@ -c builtin_.c builtin_.c : $(SRCDIR)\builtin.c +translate$E $** > $@ $(OBJDIR)\bundle$O : bundle_.c bundle.h $(TCC) -o$@ -c bundle_.c bundle_.c : $(SRCDIR)\bundle.c +translate$E $** > $@ $(OBJDIR)\cache$O : cache_.c cache.h $(TCC) -o$@ -c cache_.c cache_.c : $(SRCDIR)\cache.c +translate$E $** > $@ $(OBJDIR)\captcha$O : captcha_.c captcha.h $(TCC) -o$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c +translate$E $** > $@ |
︙ | ︙ | |||
282 283 284 285 286 287 288 289 290 291 292 293 294 295 | +translate$E $** > $@ $(OBJDIR)\finfo$O : finfo_.c finfo.h $(TCC) -o$@ -c finfo_.c finfo_.c : $(SRCDIR)\finfo.c +translate$E $** > $@ $(OBJDIR)\glob$O : glob_.c glob.h $(TCC) -o$@ -c glob_.c glob_.c : $(SRCDIR)\glob.c +translate$E $** > $@ | > > > > > > > > > > > > | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | +translate$E $** > $@ $(OBJDIR)\finfo$O : finfo_.c finfo.h $(TCC) -o$@ -c finfo_.c finfo_.c : $(SRCDIR)\finfo.c +translate$E $** > $@ $(OBJDIR)\foci$O : foci_.c foci.h $(TCC) -o$@ -c foci_.c foci_.c : $(SRCDIR)\foci.c +translate$E $** > $@ $(OBJDIR)\fusefs$O : fusefs_.c fusefs.h $(TCC) -o$@ -c fusefs_.c fusefs_.c : $(SRCDIR)\fusefs.c +translate$E $** > $@ $(OBJDIR)\glob$O : glob_.c glob.h $(TCC) -o$@ -c glob_.c glob_.c : $(SRCDIR)\glob.c +translate$E $** > $@ |
︙ | ︙ | |||
432 433 434 435 436 437 438 439 440 441 442 443 444 445 | +translate$E $** > $@ $(OBJDIR)\leaf$O : leaf_.c leaf.h $(TCC) -o$@ -c leaf_.c leaf_.c : $(SRCDIR)\leaf.c +translate$E $** > $@ $(OBJDIR)\login$O : login_.c login.h $(TCC) -o$@ -c login_.c login_.c : $(SRCDIR)\login.c +translate$E $** > $@ | > > > > > > | 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 | +translate$E $** > $@ $(OBJDIR)\leaf$O : leaf_.c leaf.h $(TCC) -o$@ -c leaf_.c leaf_.c : $(SRCDIR)\leaf.c +translate$E $** > $@ $(OBJDIR)\loadctrl$O : loadctrl_.c loadctrl.h $(TCC) -o$@ -c loadctrl_.c loadctrl_.c : $(SRCDIR)\loadctrl.c +translate$E $** > $@ $(OBJDIR)\login$O : login_.c login.h $(TCC) -o$@ -c login_.c login_.c : $(SRCDIR)\login.c +translate$E $** > $@ |
︙ | ︙ | |||
528 529 530 531 532 533 534 535 536 537 538 539 540 541 | +translate$E $** > $@ $(OBJDIR)\printf$O : printf_.c printf.h $(TCC) -o$@ -c printf_.c printf_.c : $(SRCDIR)\printf.c +translate$E $** > $@ $(OBJDIR)\rebuild$O : rebuild_.c rebuild.h $(TCC) -o$@ -c rebuild_.c rebuild_.c : $(SRCDIR)\rebuild.c +translate$E $** > $@ | > > > > > > > > > > > > | 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 | +translate$E $** > $@ $(OBJDIR)\printf$O : printf_.c printf.h $(TCC) -o$@ -c printf_.c printf_.c : $(SRCDIR)\printf.c +translate$E $** > $@ $(OBJDIR)\publish$O : publish_.c publish.h $(TCC) -o$@ -c publish_.c publish_.c : $(SRCDIR)\publish.c +translate$E $** > $@ $(OBJDIR)\purge$O : purge_.c purge.h $(TCC) -o$@ -c purge_.c purge_.c : $(SRCDIR)\purge.c +translate$E $** > $@ $(OBJDIR)\rebuild$O : rebuild_.c rebuild.h $(TCC) -o$@ -c rebuild_.c rebuild_.c : $(SRCDIR)\rebuild.c +translate$E $** > $@ |
︙ | ︙ | |||
582 583 584 585 586 587 588 589 590 591 592 593 594 595 | +translate$E $** > $@ $(OBJDIR)\shun$O : shun_.c shun.h $(TCC) -o$@ -c shun_.c shun_.c : $(SRCDIR)\shun.c +translate$E $** > $@ $(OBJDIR)\skins$O : skins_.c skins.h $(TCC) -o$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c +translate$E $** > $@ | > > > > > > | 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 | +translate$E $** > $@ $(OBJDIR)\shun$O : shun_.c shun.h $(TCC) -o$@ -c shun_.c shun_.c : $(SRCDIR)\shun.c +translate$E $** > $@ $(OBJDIR)\sitemap$O : sitemap_.c sitemap.h $(TCC) -o$@ -c sitemap_.c sitemap_.c : $(SRCDIR)\sitemap.c +translate$E $** > $@ $(OBJDIR)\skins$O : skins_.c skins.h $(TCC) -o$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c +translate$E $** > $@ |
︙ | ︙ | |||
606 607 608 609 610 611 612 613 614 615 616 617 618 619 | +translate$E $** > $@ $(OBJDIR)\stat$O : stat_.c stat.h $(TCC) -o$@ -c stat_.c stat_.c : $(SRCDIR)\stat.c +translate$E $** > $@ $(OBJDIR)\style$O : style_.c style.h $(TCC) -o$@ -c style_.c style_.c : $(SRCDIR)\style.c +translate$E $** > $@ | > > > > > > | 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 | +translate$E $** > $@ $(OBJDIR)\stat$O : stat_.c stat.h $(TCC) -o$@ -c stat_.c stat_.c : $(SRCDIR)\stat.c +translate$E $** > $@ $(OBJDIR)\statrep$O : statrep_.c statrep.h $(TCC) -o$@ -c statrep_.c statrep_.c : $(SRCDIR)\statrep.c +translate$E $** > $@ $(OBJDIR)\style$O : style_.c style.h $(TCC) -o$@ -c style_.c style_.c : $(SRCDIR)\style.c +translate$E $** > $@ |
︙ | ︙ | |||
720 721 722 723 724 725 726 727 728 729 730 731 732 733 | +translate$E $** > $@ $(OBJDIR)\wikiformat$O : wikiformat_.c wikiformat.h $(TCC) -o$@ -c wikiformat_.c wikiformat_.c : $(SRCDIR)\wikiformat.c +translate$E $** > $@ $(OBJDIR)\winhttp$O : winhttp_.c winhttp.h $(TCC) -o$@ -c winhttp_.c winhttp_.c : $(SRCDIR)\winhttp.c +translate$E $** > $@ | > > > > > > | 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 | +translate$E $** > $@ $(OBJDIR)\wikiformat$O : wikiformat_.c wikiformat.h $(TCC) -o$@ -c wikiformat_.c wikiformat_.c : $(SRCDIR)\wikiformat.c +translate$E $** > $@ $(OBJDIR)\winfile$O : winfile_.c winfile.h $(TCC) -o$@ -c winfile_.c winfile_.c : $(SRCDIR)\winfile.c +translate$E $** > $@ $(OBJDIR)\winhttp$O : winhttp_.c winhttp.h $(TCC) -o$@ -c winhttp_.c winhttp_.c : $(SRCDIR)\winhttp.c +translate$E $** > $@ |
︙ | ︙ | |||
751 752 753 754 755 756 757 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ | | | | 829 830 831 832 833 834 835 836 837 838 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers |
Changes to win/Makefile.mingw.
︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #### Enable JSON (http://www.json.org) support using "cson" # # FOSSIL_ENABLE_JSON = 1 #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # # FOSSIL_ENABLE_TCL = 1 #### Load Tcl using the stubs library mechanism # # FOSSIL_ENABLE_TCL_STUBS = 1 #### Load Tcl using the private stubs mechanism # # FOSSIL_ENABLE_TCL_PRIVATE_STUBS = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### Check if the workaround for the MinGW command line handling needs to | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | #### Enable JSON (http://www.json.org) support using "cson" # # FOSSIL_ENABLE_JSON = 1 #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # # FOSSIL_ENABLE_SSL = 1 #### Automatically build OpenSSL when building Fossil (causes rebuild # issues when building incrementally). # # FOSSIL_BUILD_SSL = 1 #### Enable TH1 scripts in embedded documentation files # # FOSSIL_ENABLE_TH1_DOCS = 1 #### Enable hooks for commands and web pages via TH1 # # FOSSIL_ENABLE_TH1_HOOKS = 1 #### Enable scripting support via Tcl/Tk # # FOSSIL_ENABLE_TCL = 1 #### Load Tcl using the stubs library mechanism # # FOSSIL_ENABLE_TCL_STUBS = 1 #### Load Tcl using the private stubs mechanism # # FOSSIL_ENABLE_TCL_PRIVATE_STUBS = 1 #### Use 'system' SQLite # # USE_SYSTEM_SQLITE = 1 #### Use the miniz compression library # # FOSSIL_ENABLE_MINIZ = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### Check if the workaround for the MinGW command line handling needs to # be enabled by default. This check may be somewhat fragile due to the # use of "findstring". # ifndef MINGW_IS_32BIT_ONLY ifeq (,$(findstring w64-mingw32,$(PREFIX))) MINGW_IS_32BIT_ONLY = 1 endif endif #### The directories where the zlib include and library files are located. # ZINCDIR = $(SRCDIR)/../compat/zlib ZLIBDIR = $(SRCDIR)/../compat/zlib #### Make an attempt to detect if Fossil is being built for the x64 processor # architecture. This check may be somewhat fragile due to "findstring". # ifndef X64 ifneq (,$(findstring x86_64-w64-mingw32,$(PREFIX))) X64 = 1 endif endif #### Determine if the optimized assembly routines provided with zlib should be # used, taking into account whether zlib is actually enabled and the target # processor architecture. # ifndef X64 SSLCONFIG = mingw ifndef FOSSIL_ENABLE_MINIZ ZLIBCONFIG = LOC="-DASMV -DASMINF" OBJA="inffas86.o match.o" LIBTARGETS = $(ZLIBDIR)/inffas86.o $(ZLIBDIR)/match.o else ZLIBCONFIG = LIBTARGETS = endif else SSLCONFIG = mingw64 ZLIBCONFIG = LIBTARGETS = endif #### Disable creation of the OpenSSL shared libraries. Also, disable support # for both SSLv2 and SSLv3 (i.e. thereby forcing the use of TLS). # SSLCONFIG += no-ssl2 no-ssl3 no-shared #### When using zlib, make sure that OpenSSL is configured to use the zlib # that Fossil knows about (i.e. the one within the source tree). # ifndef FOSSIL_ENABLE_MINIZ SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2a OPENSSLINCDIR = $(OPENSSLDIR)/include OPENSSLLIBDIR = $(OPENSSLDIR) #### Either the directory where the Tcl library is installed or the Tcl # source code directory resides (depending on the value of the macro # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, # this directory must have "include" and "lib" sub-directories. If # this points to the Tcl source code directory, this directory must # have "generic" and "win" sub-directories. The recommended usage |
︙ | ︙ | |||
128 129 130 131 132 133 134 | #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # | | > > > > > > | > > > > > > > > > > | | > > > > > > > > > > > > | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # TCC = $(PREFIX)gcc -Os -Wall #### When not using the miniz compression library, zlib is required. # ifndef FOSSIL_ENABLE_MINIZ TCC += -L$(ZLIBDIR) -I$(ZINCDIR) endif #### Add the necessary command line options to build with debugging # symbols, if enabled. # ifdef FOSSIL_ENABLE_SYMBOLS TCC += -g endif #### Compile resources for use in building executables that will run # on the target platform. # RCC = $(PREFIX)windres -I$(SRCDIR) ifndef FOSSIL_ENABLE_MINIZ RCC += -I$(ZINCDIR) endif # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -L$(OPENSSLLIBDIR) -I$(OPENSSLINCDIR) RCC += -I$(OPENSSLINCDIR) endif # With Tcl support ifdef FOSSIL_ENABLE_TCL ifdef FOSSIL_TCL_SOURCE TCC += -L$(TCLSRCDIR)/win -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win RCC += -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win else TCC += -L$(TCLLIBDIR) -I$(TCLINCDIR) RCC += -I$(TCLINCDIR) endif endif # With miniz (i.e. instead of zlib) ifdef FOSSIL_ENABLE_MINIZ TCC += -DFOSSIL_ENABLE_MINIZ=1 RCC += -DFOSSIL_ENABLE_MINIZ=1 endif # With MinGW command line handling workaround ifdef MINGW_IS_32BIT_ONLY TCC += -DBROKEN_MINGW_CMDLINE=1 RCC += -DBROKEN_MINGW_CMDLINE=1 endif # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -DFOSSIL_ENABLE_SSL=1 RCC += -DFOSSIL_ENABLE_SSL=1 endif # With TH1 embedded docs support ifdef FOSSIL_ENABLE_TH1_DOCS TCC += -DFOSSIL_ENABLE_TH1_DOCS=1 RCC += -DFOSSIL_ENABLE_TH1_DOCS=1 endif # With TH1 hook support ifdef FOSSIL_ENABLE_TH1_HOOKS TCC += -DFOSSIL_ENABLE_TH1_HOOKS=1 RCC += -DFOSSIL_ENABLE_TH1_HOOKS=1 endif # With Tcl support ifdef FOSSIL_ENABLE_TCL TCC += -DFOSSIL_ENABLE_TCL=1 RCC += -DFOSSIL_ENABLE_TCL=1 # Either statically linked or via stubs ifdef FOSSIL_ENABLE_TCL_STUBS |
︙ | ︙ | |||
200 201 202 203 204 205 206 | endif #### We add the -static option here so that we can build a static # executable that will run in a chroot jail. # LIB = -static | | > > > > > > > | > | > | > > > > > > | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | endif #### We add the -static option here so that we can build a static # executable that will run in a chroot jail. # LIB = -static #### MinGW: If available, use the Unicode capable runtime startup code. # ifndef MINGW_IS_32BIT_ONLY LIB += -municode endif #### SQLite: If enabled, use the system SQLite library. # ifdef USE_SYSTEM_SQLITE LIB += -lsqlite3 endif #### OpenSSL: Add the necessary libraries required, if enabled. # ifdef FOSSIL_ENABLE_SSL LIB += -lssl -lcrypto -lgdi32 endif #### Tcl: Add the necessary libraries required, if enabled. # ifdef FOSSIL_ENABLE_TCL LIB += $(LIBTCL) endif #### Extra arguments for linking the finished binary. Fossil needs # to link against the Z-Lib compression library. There are no # other mandatory dependencies. # LIB += -lmingwex #### When not using the miniz compression library, zlib is required. # ifndef FOSSIL_ENABLE_MINIZ LIB += -lz endif #### These libraries MUST appear in the same order as they do for Tcl # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL ifdef FOSSIL_ENABLE_TCL_STUBS LIB += -lkernel32 -lws2_32 |
︙ | ︙ | |||
241 242 243 244 245 246 247 | #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location # | | > > > > > > > | 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location # MAKENSIS = "$(PROGRAMFILES)\NSIS\MakeNSIS.exe" #### Inno Setup executable location # INNOSETUP = "$(PROGRAMFILES)\Inno Setup 5\ISCC.exe" #### Include a configuration file that can override any one of these settings. # -include config.w32 # STOP HERE # You should not need to change anything below this line #-------------------------------------------------------- XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/builtin.c \ $(SRCDIR)/bundle.c \ $(SRCDIR)/cache.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/comformat.c \ |
︙ | ︙ | |||
282 283 284 285 286 287 288 289 290 291 292 293 294 295 | $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ | > > | 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ |
︙ | ︙ | |||
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | $(SRCDIR)/json_report.c \ $(SRCDIR)/json_status.c \ $(SRCDIR)/json_tag.c \ $(SRCDIR)/json_timeline.c \ $(SRCDIR)/json_user.c \ $(SRCDIR)/json_wiki.c \ $(SRCDIR)/leaf.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/path.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ $(SRCDIR)/tar.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ $(SRCDIR)/unicode.c \ $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | $(SRCDIR)/json_report.c \ $(SRCDIR)/json_status.c \ $(SRCDIR)/json_tag.c \ $(SRCDIR)/json_timeline.c \ $(SRCDIR)/json_user.c \ $(SRCDIR)/json_wiki.c \ $(SRCDIR)/leaf.c \ $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/path.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/publish.c \ $(SRCDIR)/purge.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ $(SRCDIR)/tar.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ $(SRCDIR)/unicode.c \ $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ $(SRCDIR)/../skins/aht/details.txt \ $(SRCDIR)/../skins/black_and_white/css.txt \ $(SRCDIR)/../skins/black_and_white/details.txt \ $(SRCDIR)/../skins/black_and_white/footer.txt \ $(SRCDIR)/../skins/black_and_white/header.txt \ $(SRCDIR)/../skins/blitz/css.txt \ $(SRCDIR)/../skins/blitz/details.txt \ $(SRCDIR)/../skins/blitz/footer.txt \ $(SRCDIR)/../skins/blitz/header.txt \ $(SRCDIR)/../skins/blitz/ticket.txt \ $(SRCDIR)/../skins/blitz_no_logo/css.txt \ $(SRCDIR)/../skins/blitz_no_logo/details.txt \ $(SRCDIR)/../skins/blitz_no_logo/footer.txt \ $(SRCDIR)/../skins/blitz_no_logo/header.txt \ $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \ $(SRCDIR)/../skins/default/css.txt \ $(SRCDIR)/../skins/default/details.txt \ $(SRCDIR)/../skins/default/footer.txt \ $(SRCDIR)/../skins/default/header.txt \ $(SRCDIR)/../skins/eagle/css.txt \ $(SRCDIR)/../skins/eagle/details.txt \ $(SRCDIR)/../skins/eagle/footer.txt \ $(SRCDIR)/../skins/eagle/header.txt \ $(SRCDIR)/../skins/enhanced1/css.txt \ $(SRCDIR)/../skins/enhanced1/details.txt \ $(SRCDIR)/../skins/enhanced1/footer.txt \ $(SRCDIR)/../skins/enhanced1/header.txt \ $(SRCDIR)/../skins/khaki/css.txt \ $(SRCDIR)/../skins/khaki/details.txt \ $(SRCDIR)/../skins/khaki/footer.txt \ $(SRCDIR)/../skins/khaki/header.txt \ $(SRCDIR)/../skins/original/css.txt \ $(SRCDIR)/../skins/original/details.txt \ $(SRCDIR)/../skins/original/footer.txt \ $(SRCDIR)/../skins/original/header.txt \ $(SRCDIR)/../skins/plain_gray/css.txt \ $(SRCDIR)/../skins/plain_gray/details.txt \ $(SRCDIR)/../skins/plain_gray/footer.txt \ $(SRCDIR)/../skins/plain_gray/header.txt \ $(SRCDIR)/../skins/rounded1/css.txt \ $(SRCDIR)/../skins/rounded1/details.txt \ $(SRCDIR)/../skins/rounded1/footer.txt \ $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/../skins/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/markdown.md TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/builtin_.c \ $(OBJDIR)/bundle_.c \ $(OBJDIR)/cache_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ |
︙ | ︙ | |||
391 392 393 394 395 396 397 398 399 400 401 402 403 404 | $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ | > > | 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ |
︙ | ︙ | |||
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 | $(OBJDIR)/json_report_.c \ $(OBJDIR)/json_status_.c \ $(OBJDIR)/json_tag_.c \ $(OBJDIR)/json_timeline_.c \ $(OBJDIR)/json_user_.c \ $(OBJDIR)/json_wiki_.c \ $(OBJDIR)/leaf_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/pivot_.c \ $(OBJDIR)/popen_.c \ $(OBJDIR)/pqueue_.c \ $(OBJDIR)/printf_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ $(OBJDIR)/tar_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ $(OBJDIR)/unicode_.c \ $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ | > > > > > > > > > | 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 | $(OBJDIR)/json_report_.c \ $(OBJDIR)/json_status_.c \ $(OBJDIR)/json_tag_.c \ $(OBJDIR)/json_timeline_.c \ $(OBJDIR)/json_user_.c \ $(OBJDIR)/json_wiki_.c \ $(OBJDIR)/leaf_.c \ $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/pivot_.c \ $(OBJDIR)/popen_.c \ $(OBJDIR)/pqueue_.c \ $(OBJDIR)/printf_.c \ $(OBJDIR)/publish_.c \ $(OBJDIR)/purge_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ $(OBJDIR)/tar_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ $(OBJDIR)/unicode_.c \ $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/builtin.o \ $(OBJDIR)/bundle.o \ $(OBJDIR)/cache.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ |
︙ | ︙ | |||
500 501 502 503 504 505 506 507 508 509 510 511 512 513 | $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ | > > | 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 | $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ |
︙ | ︙ | |||
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 | $(OBJDIR)/json_report.o \ $(OBJDIR)/json_status.o \ $(OBJDIR)/json_tag.o \ $(OBJDIR)/json_timeline.o \ $(OBJDIR)/json_user.o \ $(OBJDIR)/json_wiki.o \ $(OBJDIR)/leaf.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/path.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ $(OBJDIR)/tar.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ $(OBJDIR)/unicode.o \ $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o | > > > > > > | > | | | | > > > > | | | | > > > > > > > > | | | | | | > > > | | > > > | | > > > > > > > | > > > > > > > > > | > > > | > | | > > > > > > > > > > > | | > | > > > > > > | | > > > | > > > | > > > | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 | $(OBJDIR)/json_report.o \ $(OBJDIR)/json_status.o \ $(OBJDIR)/json_tag.o \ $(OBJDIR)/json_timeline.o \ $(OBJDIR)/json_user.o \ $(OBJDIR)/json_wiki.o \ $(OBJDIR)/leaf.o \ $(OBJDIR)/loadctrl.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/path.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/publish.o \ $(OBJDIR)/purge.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ $(OBJDIR)/tar.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ $(OBJDIR)/unicode.o \ $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o APPNAME = fossil.exe APPTARGETS = #### If the USE_WINDOWS variable exists, it is assumed that we are building # inside of a Windows-style shell; otherwise, it is assumed that we are # building inside of a Unix-style shell. Note that the "move" command is # broken when attempting to use it from the Windows shell via MinGW make # because the SHELL variable is only used for certain commands that are # recognized internally by make. # ifdef USE_WINDOWS TRANSLATE = $(subst /,\,$(OBJDIR)/translate.exe) MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe) MKINDEX = $(subst /,\,$(OBJDIR)/mkindex.exe) MKBUILTIN = $(subst /,\,$(OBJDIR)/mkbuiltin.exe) MKVERSION = $(subst /,\,$(OBJDIR)/mkversion.exe) CODECHECK1 = $(subst /,\,$(OBJDIR)/codecheck1.exe) CAT = type CP = copy GREP = find MV = copy RM = del /Q MKDIR = -mkdir RMDIR = rmdir /S /Q else TRANSLATE = $(OBJDIR)/translate.exe MAKEHEADERS = $(OBJDIR)/makeheaders.exe MKINDEX = $(OBJDIR)/mkindex.exe MKBUILTIN = $(OBJDIR)/mkbuiltin.exe MKVERSION = $(OBJDIR)/mkversion.exe CODECHECK1 = $(OBJDIR)/codecheck1.exe CAT = cat CP = cp GREP = grep MV = mv RM = rm -f MKDIR = -mkdir -p RMDIR = rm -rf endif all: $(OBJDIR) $(APPNAME) $(OBJDIR)/fossil.o: $(SRCDIR)/../win/fossil.rc $(OBJDIR)/VERSION.h ifdef USE_WINDOWS $(CAT) $(subst /,\,$(SRCDIR)\miniz.c) | $(GREP) "define MZ_VERSION" > $(subst /,\,$(OBJDIR)\minizver.h) $(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.rc) $(subst /,\,$(OBJDIR)) $(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.ico) $(subst /,\,$(OBJDIR)) $(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.exe.manifest) $(subst /,\,$(OBJDIR)) else $(CAT) $(SRCDIR)/miniz.c | $(GREP) "define MZ_VERSION" > $(OBJDIR)/minizver.h $(CP) $(SRCDIR)/../win/fossil.rc $(OBJDIR) $(CP) $(SRCDIR)/../win/fossil.ico $(OBJDIR) $(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR) endif $(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o install: $(OBJDIR) $(APPNAME) ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(INSTALLDIR)) $(MV) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR)) else $(MKDIR) $(INSTALLDIR) $(MV) $(APPNAME) $(INSTALLDIR) endif $(OBJDIR): ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(OBJDIR)) else $(MKDIR) $(OBJDIR) endif $(TRANSLATE): $(SRCDIR)/translate.c $(BCC) -o $@ $(SRCDIR)/translate.c $(MAKEHEADERS): $(SRCDIR)/makeheaders.c $(BCC) -o $@ $(SRCDIR)/makeheaders.c $(MKINDEX): $(SRCDIR)/mkindex.c $(BCC) -o $@ $(SRCDIR)/mkindex.c $(MKBUILTIN): $(SRCDIR)/mkbuiltin.c $(BCC) -o $@ $(SRCDIR)/mkbuiltin.c $(MKVERSION): $(SRCDIR)/mkversion.c $(BCC) -o $@ $(SRCDIR)/mkversion.c $(CODECHECK1): $(SRCDIR)/codecheck1.c $(BCC) -o $@ $(SRCDIR)/codecheck1.c # WARNING. DANGER. Running the test suite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set # to 1. If it is set to 1, then there is no need to build or link # the sqlite3.o object. Instead, the system SQLite will be linked # using -lsqlite3. SQLITE3_OBJ.1 = SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ. = $(SQLITE3_OBJ.0) # The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or # set to 1. If it is set to 1, the miniz library included in the # source tree should be used; otherwise, it should not. MINIZ_OBJ.0 = MINIZ_OBJ.1 = $(OBJDIR)/miniz.o MINIZ_OBJ. = $(MINIZ_OBJ.0) EXTRAOBJ = \ $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \ $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ $(OBJDIR)/th_lang.o \ $(OBJDIR)/th_tcl.o \ $(OBJDIR)/cson_amalgamation.o zlib: $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) $(ZLIBCONFIG) -f win32/Makefile.gcc libz.a clean-zlib: $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc clean $(ZLIBDIR)/inffas86.o: $(TCC) -c -o $@ -DASMINF -I$(ZLIBDIR) -O3 $(ZLIBDIR)/contrib/inflate86/inffas86.c $(ZLIBDIR)/match.o: $(TCC) -c -o $@ -DASMV $(ZLIBDIR)/contrib/asm686/match.S ifndef FOSSIL_ENABLE_MINIZ LIBTARGETS += zlib endif openssl: $(LIBTARGETS) cd $(OPENSSLLIBDIR);./Configure --cross-compile-prefix=$(PREFIX) $(SSLCONFIG) $(MAKE) -C $(OPENSSLLIBDIR) build_libs clean-openssl: $(MAKE) -C $(OPENSSLLIBDIR) clean tcl: cd $(TCLSRCDIR)/win;./configure $(MAKE) -C $(TCLSRCDIR)/win $(TCLTARGET) clean-tcl: $(MAKE) -C $(TCLSRCDIR)/win distclean APPTARGETS += $(LIBTARGETS) ifdef FOSSIL_BUILD_SSL APPTARGETS += openssl endif $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(CODECHECK1) $(TRANS_SRC) $(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o # This rule prevents make from using its default rules to try build # an executable named "manifest" out of the file named "manifest.c" # $(SRCDIR)/../manifest: # noop clean: ifdef USE_WINDOWS $(RM) $(subst /,\,$(APPNAME)) $(RMDIR) $(subst /,\,$(OBJDIR)) else $(RM) $(APPNAME) $(RMDIR) $(OBJDIR) endif setup: $(OBJDIR) $(APPNAME) $(MAKENSIS) ./setup/fossil.nsi innosetup: $(OBJDIR) $(APPNAME) $(INNOSETUP) ./setup/fossil.iss -DAppVersion=$(shell $(CAT) ./VERSION) $(OBJDIR)/page_index.h: $(TRANS_SRC) $(MKINDEX) $(MKINDEX) $(TRANS_SRC) >$@ $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \ $(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \ $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ |
︙ | ︙ | |||
739 740 741 742 743 744 745 746 747 748 749 750 751 752 | $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \ $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \ $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \ | > > | 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 | $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \ $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \ $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \ |
︙ | ︙ | |||
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 | $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h \ $(OBJDIR)/json_status_.c:$(OBJDIR)/json_status.h \ $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h \ $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h \ $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h \ $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h \ $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ $(SRCDIR)/sqlite3.h \ $(SRCDIR)/th.h \ $(OBJDIR)/VERSION.h echo Done >$(OBJDIR)/headers $(OBJDIR)/headers: Makefile Makefile: | > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > | | | | | | | | | | | | > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | < | | > > > | 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 | $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h \ $(OBJDIR)/json_status_.c:$(OBJDIR)/json_status.h \ $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h \ $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h \ $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h \ $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h \ $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h \ $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ $(OBJDIR)/publish_.c:$(OBJDIR)/publish.h \ $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \ $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ $(SRCDIR)/sqlite3.h \ $(SRCDIR)/th.h \ $(OBJDIR)/VERSION.h echo Done >$(OBJDIR)/headers $(OBJDIR)/headers: Makefile Makefile: $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/add.c >$@ $(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c $(OBJDIR)/add.h: $(OBJDIR)/headers $(OBJDIR)/allrepo_.c: $(SRCDIR)/allrepo.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/allrepo.c >$@ $(OBJDIR)/allrepo.o: $(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/allrepo.o -c $(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h: $(OBJDIR)/headers $(OBJDIR)/attach_.c: $(SRCDIR)/attach.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/attach.c >$@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers $(OBJDIR)/bag_.c: $(SRCDIR)/bag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bag.c >$@ $(OBJDIR)/bag.o: $(OBJDIR)/bag_.c $(OBJDIR)/bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c $(OBJDIR)/bag.h: $(OBJDIR)/headers $(OBJDIR)/bisect_.c: $(SRCDIR)/bisect.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bisect.c >$@ $(OBJDIR)/bisect.o: $(OBJDIR)/bisect_.c $(OBJDIR)/bisect.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bisect.o -c $(OBJDIR)/bisect_.c $(OBJDIR)/bisect.h: $(OBJDIR)/headers $(OBJDIR)/blob_.c: $(SRCDIR)/blob.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/blob.c >$@ $(OBJDIR)/blob.o: $(OBJDIR)/blob_.c $(OBJDIR)/blob.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/blob.o -c $(OBJDIR)/blob_.c $(OBJDIR)/blob.h: $(OBJDIR)/headers $(OBJDIR)/branch_.c: $(SRCDIR)/branch.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/branch.c >$@ $(OBJDIR)/branch.o: $(OBJDIR)/branch_.c $(OBJDIR)/branch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/branch.o -c $(OBJDIR)/branch_.c $(OBJDIR)/branch.h: $(OBJDIR)/headers $(OBJDIR)/browse_.c: $(SRCDIR)/browse.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/browse.c >$@ $(OBJDIR)/browse.o: $(OBJDIR)/browse_.c $(OBJDIR)/browse.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/browse.o -c $(OBJDIR)/browse_.c $(OBJDIR)/browse.h: $(OBJDIR)/headers $(OBJDIR)/builtin_.c: $(SRCDIR)/builtin.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/builtin.c >$@ $(OBJDIR)/builtin.o: $(OBJDIR)/builtin_.c $(OBJDIR)/builtin.h $(OBJDIR)/builtin_data.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/builtin.o -c $(OBJDIR)/builtin_.c $(OBJDIR)/builtin.h: $(OBJDIR)/headers $(OBJDIR)/bundle_.c: $(SRCDIR)/bundle.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bundle.c >$@ $(OBJDIR)/bundle.o: $(OBJDIR)/bundle_.c $(OBJDIR)/bundle.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bundle.o -c $(OBJDIR)/bundle_.c $(OBJDIR)/bundle.h: $(OBJDIR)/headers $(OBJDIR)/cache_.c: $(SRCDIR)/cache.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cache.c >$@ $(OBJDIR)/cache.o: $(OBJDIR)/cache_.c $(OBJDIR)/cache.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cache.o -c $(OBJDIR)/cache_.c $(OBJDIR)/cache.h: $(OBJDIR)/headers $(OBJDIR)/captcha_.c: $(SRCDIR)/captcha.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/captcha.c >$@ $(OBJDIR)/captcha.o: $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h: $(OBJDIR)/headers $(OBJDIR)/cgi_.c: $(SRCDIR)/cgi.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cgi.c >$@ $(OBJDIR)/cgi.o: $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h: $(OBJDIR)/headers $(OBJDIR)/checkin_.c: $(SRCDIR)/checkin.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/checkin.c >$@ $(OBJDIR)/checkin.o: $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h: $(OBJDIR)/headers $(OBJDIR)/checkout_.c: $(SRCDIR)/checkout.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/checkout.c >$@ $(OBJDIR)/checkout.o: $(OBJDIR)/checkout_.c $(OBJDIR)/checkout.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/checkout.o -c $(OBJDIR)/checkout_.c $(OBJDIR)/checkout.h: $(OBJDIR)/headers $(OBJDIR)/clearsign_.c: $(SRCDIR)/clearsign.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/clearsign.c >$@ $(OBJDIR)/clearsign.o: $(OBJDIR)/clearsign_.c $(OBJDIR)/clearsign.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/clearsign.o -c $(OBJDIR)/clearsign_.c $(OBJDIR)/clearsign.h: $(OBJDIR)/headers $(OBJDIR)/clone_.c: $(SRCDIR)/clone.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/clone.c >$@ $(OBJDIR)/clone.o: $(OBJDIR)/clone_.c $(OBJDIR)/clone.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c $(OBJDIR)/clone.h: $(OBJDIR)/headers $(OBJDIR)/comformat_.c: $(SRCDIR)/comformat.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/comformat.c >$@ $(OBJDIR)/comformat.o: $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/comformat.o -c $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h: $(OBJDIR)/headers $(OBJDIR)/configure_.c: $(SRCDIR)/configure.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/configure.c >$@ $(OBJDIR)/configure.o: $(OBJDIR)/configure_.c $(OBJDIR)/configure.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/configure.o -c $(OBJDIR)/configure_.c $(OBJDIR)/configure.h: $(OBJDIR)/headers $(OBJDIR)/content_.c: $(SRCDIR)/content.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/content.c >$@ $(OBJDIR)/content.o: $(OBJDIR)/content_.c $(OBJDIR)/content.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/content.o -c $(OBJDIR)/content_.c $(OBJDIR)/content.h: $(OBJDIR)/headers $(OBJDIR)/db_.c: $(SRCDIR)/db.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/db.c >$@ $(OBJDIR)/db.o: $(OBJDIR)/db_.c $(OBJDIR)/db.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/db.o -c $(OBJDIR)/db_.c $(OBJDIR)/db.h: $(OBJDIR)/headers $(OBJDIR)/delta_.c: $(SRCDIR)/delta.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/delta.c >$@ $(OBJDIR)/delta.o: $(OBJDIR)/delta_.c $(OBJDIR)/delta.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/delta.o -c $(OBJDIR)/delta_.c $(OBJDIR)/delta.h: $(OBJDIR)/headers $(OBJDIR)/deltacmd_.c: $(SRCDIR)/deltacmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltacmd.c >$@ $(OBJDIR)/deltacmd.o: $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h: $(OBJDIR)/headers $(OBJDIR)/descendants_.c: $(SRCDIR)/descendants.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/descendants.c >$@ $(OBJDIR)/descendants.o: $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h: $(OBJDIR)/headers $(OBJDIR)/diff_.c: $(SRCDIR)/diff.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/diff.c >$@ $(OBJDIR)/diff.o: $(OBJDIR)/diff_.c $(OBJDIR)/diff.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/diff.o -c $(OBJDIR)/diff_.c $(OBJDIR)/diff.h: $(OBJDIR)/headers $(OBJDIR)/diffcmd_.c: $(SRCDIR)/diffcmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/diffcmd.c >$@ $(OBJDIR)/diffcmd.o: $(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/diffcmd.o -c $(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h: $(OBJDIR)/headers $(OBJDIR)/doc_.c: $(SRCDIR)/doc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/doc.c >$@ $(OBJDIR)/doc.o: $(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c $(OBJDIR)/doc.h: $(OBJDIR)/headers $(OBJDIR)/encode_.c: $(SRCDIR)/encode.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/encode.c >$@ $(OBJDIR)/encode.o: $(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c $(OBJDIR)/encode.h: $(OBJDIR)/headers $(OBJDIR)/event_.c: $(SRCDIR)/event.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/event.c >$@ $(OBJDIR)/event.o: $(OBJDIR)/event_.c $(OBJDIR)/event.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/event.o -c $(OBJDIR)/event_.c $(OBJDIR)/event.h: $(OBJDIR)/headers $(OBJDIR)/export_.c: $(SRCDIR)/export.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/export.c >$@ $(OBJDIR)/export.o: $(OBJDIR)/export_.c $(OBJDIR)/export.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/export.o -c $(OBJDIR)/export_.c $(OBJDIR)/export.h: $(OBJDIR)/headers $(OBJDIR)/file_.c: $(SRCDIR)/file.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/file.c >$@ $(OBJDIR)/file.o: $(OBJDIR)/file_.c $(OBJDIR)/file.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c $(OBJDIR)/file.h: $(OBJDIR)/headers $(OBJDIR)/finfo_.c: $(SRCDIR)/finfo.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/finfo.c >$@ $(OBJDIR)/finfo.o: $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/finfo.o -c $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h: $(OBJDIR)/headers $(OBJDIR)/foci_.c: $(SRCDIR)/foci.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/foci.c >$@ $(OBJDIR)/foci.o: $(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c $(OBJDIR)/foci.h: $(OBJDIR)/headers $(OBJDIR)/fusefs_.c: $(SRCDIR)/fusefs.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fusefs.c >$@ $(OBJDIR)/fusefs.o: $(OBJDIR)/fusefs_.c $(OBJDIR)/fusefs.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fusefs.o -c $(OBJDIR)/fusefs_.c $(OBJDIR)/fusefs.h: $(OBJDIR)/headers $(OBJDIR)/glob_.c: $(SRCDIR)/glob.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/glob.c >$@ $(OBJDIR)/glob.o: $(OBJDIR)/glob_.c $(OBJDIR)/glob.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/glob.o -c $(OBJDIR)/glob_.c $(OBJDIR)/glob.h: $(OBJDIR)/headers $(OBJDIR)/graph_.c: $(SRCDIR)/graph.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/graph.c >$@ $(OBJDIR)/graph.o: $(OBJDIR)/graph_.c $(OBJDIR)/graph.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/graph.o -c $(OBJDIR)/graph_.c $(OBJDIR)/graph.h: $(OBJDIR)/headers $(OBJDIR)/gzip_.c: $(SRCDIR)/gzip.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/gzip.c >$@ $(OBJDIR)/gzip.o: $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h: $(OBJDIR)/headers $(OBJDIR)/http_.c: $(SRCDIR)/http.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http.c >$@ $(OBJDIR)/http.o: $(OBJDIR)/http_.c $(OBJDIR)/http.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c $(OBJDIR)/http.h: $(OBJDIR)/headers $(OBJDIR)/http_socket_.c: $(SRCDIR)/http_socket.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http_socket.c >$@ $(OBJDIR)/http_socket.o: $(OBJDIR)/http_socket_.c $(OBJDIR)/http_socket.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_socket.o -c $(OBJDIR)/http_socket_.c $(OBJDIR)/http_socket.h: $(OBJDIR)/headers $(OBJDIR)/http_ssl_.c: $(SRCDIR)/http_ssl.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http_ssl.c >$@ $(OBJDIR)/http_ssl.o: $(OBJDIR)/http_ssl_.c $(OBJDIR)/http_ssl.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_ssl.o -c $(OBJDIR)/http_ssl_.c $(OBJDIR)/http_ssl.h: $(OBJDIR)/headers $(OBJDIR)/http_transport_.c: $(SRCDIR)/http_transport.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http_transport.c >$@ $(OBJDIR)/http_transport.o: $(OBJDIR)/http_transport_.c $(OBJDIR)/http_transport.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_transport.o -c $(OBJDIR)/http_transport_.c $(OBJDIR)/http_transport.h: $(OBJDIR)/headers $(OBJDIR)/import_.c: $(SRCDIR)/import.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/import.c >$@ $(OBJDIR)/import.o: $(OBJDIR)/import_.c $(OBJDIR)/import.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/import.o -c $(OBJDIR)/import_.c $(OBJDIR)/import.h: $(OBJDIR)/headers $(OBJDIR)/info_.c: $(SRCDIR)/info.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/info.c >$@ $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c $(OBJDIR)/info.h: $(OBJDIR)/headers $(OBJDIR)/json_.c: $(SRCDIR)/json.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json.c >$@ $(OBJDIR)/json.o: $(OBJDIR)/json_.c $(OBJDIR)/json.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c $(OBJDIR)/json.h: $(OBJDIR)/headers $(OBJDIR)/json_artifact_.c: $(SRCDIR)/json_artifact.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_artifact.c >$@ $(OBJDIR)/json_artifact.o: $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h: $(OBJDIR)/headers $(OBJDIR)/json_branch_.c: $(SRCDIR)/json_branch.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_branch.c >$@ $(OBJDIR)/json_branch.o: $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h: $(OBJDIR)/headers $(OBJDIR)/json_config_.c: $(SRCDIR)/json_config.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_config.c >$@ $(OBJDIR)/json_config.o: $(OBJDIR)/json_config_.c $(OBJDIR)/json_config.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_config.o -c $(OBJDIR)/json_config_.c $(OBJDIR)/json_config.h: $(OBJDIR)/headers $(OBJDIR)/json_diff_.c: $(SRCDIR)/json_diff.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_diff.c >$@ $(OBJDIR)/json_diff.o: $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h: $(OBJDIR)/headers $(OBJDIR)/json_dir_.c: $(SRCDIR)/json_dir.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_dir.c >$@ $(OBJDIR)/json_dir.o: $(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_dir.o -c $(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h: $(OBJDIR)/headers $(OBJDIR)/json_finfo_.c: $(SRCDIR)/json_finfo.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_finfo.c >$@ $(OBJDIR)/json_finfo.o: $(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_finfo.o -c $(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h: $(OBJDIR)/headers $(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_login.c >$@ $(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h: $(OBJDIR)/headers $(OBJDIR)/json_query_.c: $(SRCDIR)/json_query.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_query.c >$@ $(OBJDIR)/json_query.o: $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h: $(OBJDIR)/headers $(OBJDIR)/json_report_.c: $(SRCDIR)/json_report.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_report.c >$@ $(OBJDIR)/json_report.o: $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h: $(OBJDIR)/headers $(OBJDIR)/json_status_.c: $(SRCDIR)/json_status.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_status.c >$@ $(OBJDIR)/json_status.o: $(OBJDIR)/json_status_.c $(OBJDIR)/json_status.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_status.o -c $(OBJDIR)/json_status_.c $(OBJDIR)/json_status.h: $(OBJDIR)/headers $(OBJDIR)/json_tag_.c: $(SRCDIR)/json_tag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_tag.c >$@ $(OBJDIR)/json_tag.o: $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h: $(OBJDIR)/headers $(OBJDIR)/json_timeline_.c: $(SRCDIR)/json_timeline.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_timeline.c >$@ $(OBJDIR)/json_timeline.o: $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h: $(OBJDIR)/headers $(OBJDIR)/json_user_.c: $(SRCDIR)/json_user.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_user.c >$@ $(OBJDIR)/json_user.o: $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h: $(OBJDIR)/headers $(OBJDIR)/json_wiki_.c: $(SRCDIR)/json_wiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_wiki.c >$@ $(OBJDIR)/json_wiki.o: $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h: $(OBJDIR)/headers $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/leaf.c >$@ $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h: $(OBJDIR)/headers $(OBJDIR)/loadctrl_.c: $(SRCDIR)/loadctrl.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/loadctrl.c >$@ $(OBJDIR)/loadctrl.o: $(OBJDIR)/loadctrl_.c $(OBJDIR)/loadctrl.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/loadctrl.o -c $(OBJDIR)/loadctrl_.c $(OBJDIR)/loadctrl.h: $(OBJDIR)/headers $(OBJDIR)/login_.c: $(SRCDIR)/login.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/login.c >$@ $(OBJDIR)/login.o: $(OBJDIR)/login_.c $(OBJDIR)/login.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/login.o -c $(OBJDIR)/login_.c $(OBJDIR)/login.h: $(OBJDIR)/headers $(OBJDIR)/lookslike_.c: $(SRCDIR)/lookslike.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/lookslike.c >$@ $(OBJDIR)/lookslike.o: $(OBJDIR)/lookslike_.c $(OBJDIR)/lookslike.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/lookslike.o -c $(OBJDIR)/lookslike_.c $(OBJDIR)/lookslike.h: $(OBJDIR)/headers $(OBJDIR)/main_.c: $(SRCDIR)/main.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/main.c >$@ $(OBJDIR)/main.o: $(OBJDIR)/main_.c $(OBJDIR)/main.h $(OBJDIR)/page_index.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/main.o -c $(OBJDIR)/main_.c $(OBJDIR)/main.h: $(OBJDIR)/headers $(OBJDIR)/manifest_.c: $(SRCDIR)/manifest.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/manifest.c >$@ $(OBJDIR)/manifest.o: $(OBJDIR)/manifest_.c $(OBJDIR)/manifest.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/manifest.o -c $(OBJDIR)/manifest_.c $(OBJDIR)/manifest.h: $(OBJDIR)/headers $(OBJDIR)/markdown_.c: $(SRCDIR)/markdown.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/markdown.c >$@ $(OBJDIR)/markdown.o: $(OBJDIR)/markdown_.c $(OBJDIR)/markdown.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown.o -c $(OBJDIR)/markdown_.c $(OBJDIR)/markdown.h: $(OBJDIR)/headers $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c $(OBJDIR)/md5.h: $(OBJDIR)/headers $(OBJDIR)/merge_.c: $(SRCDIR)/merge.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/merge.c >$@ $(OBJDIR)/merge.o: $(OBJDIR)/merge_.c $(OBJDIR)/merge.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/merge.o -c $(OBJDIR)/merge_.c $(OBJDIR)/merge.h: $(OBJDIR)/headers $(OBJDIR)/merge3_.c: $(SRCDIR)/merge3.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/merge3.c >$@ $(OBJDIR)/merge3.o: $(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h: $(OBJDIR)/headers $(OBJDIR)/moderate_.c: $(SRCDIR)/moderate.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/moderate.c >$@ $(OBJDIR)/moderate.o: $(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h: $(OBJDIR)/headers $(OBJDIR)/name_.c: $(SRCDIR)/name.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/name.c >$@ $(OBJDIR)/name.o: $(OBJDIR)/name_.c $(OBJDIR)/name.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c $(OBJDIR)/name.h: $(OBJDIR)/headers $(OBJDIR)/path_.c: $(SRCDIR)/path.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/path.c >$@ $(OBJDIR)/path.o: $(OBJDIR)/path_.c $(OBJDIR)/path.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/path.o -c $(OBJDIR)/path_.c $(OBJDIR)/path.h: $(OBJDIR)/headers $(OBJDIR)/pivot_.c: $(SRCDIR)/pivot.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/pivot.c >$@ $(OBJDIR)/pivot.o: $(OBJDIR)/pivot_.c $(OBJDIR)/pivot.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pivot.o -c $(OBJDIR)/pivot_.c $(OBJDIR)/pivot.h: $(OBJDIR)/headers $(OBJDIR)/popen_.c: $(SRCDIR)/popen.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/popen.c >$@ $(OBJDIR)/popen.o: $(OBJDIR)/popen_.c $(OBJDIR)/popen.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/popen.o -c $(OBJDIR)/popen_.c $(OBJDIR)/popen.h: $(OBJDIR)/headers $(OBJDIR)/pqueue_.c: $(SRCDIR)/pqueue.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/pqueue.c >$@ $(OBJDIR)/pqueue.o: $(OBJDIR)/pqueue_.c $(OBJDIR)/pqueue.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pqueue.o -c $(OBJDIR)/pqueue_.c $(OBJDIR)/pqueue.h: $(OBJDIR)/headers $(OBJDIR)/printf_.c: $(SRCDIR)/printf.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/printf.c >$@ $(OBJDIR)/printf.o: $(OBJDIR)/printf_.c $(OBJDIR)/printf.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/printf.o -c $(OBJDIR)/printf_.c $(OBJDIR)/printf.h: $(OBJDIR)/headers $(OBJDIR)/publish_.c: $(SRCDIR)/publish.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/publish.c >$@ $(OBJDIR)/publish.o: $(OBJDIR)/publish_.c $(OBJDIR)/publish.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/publish.o -c $(OBJDIR)/publish_.c $(OBJDIR)/publish.h: $(OBJDIR)/headers $(OBJDIR)/purge_.c: $(SRCDIR)/purge.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/purge.c >$@ $(OBJDIR)/purge.o: $(OBJDIR)/purge_.c $(OBJDIR)/purge.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/purge.o -c $(OBJDIR)/purge_.c $(OBJDIR)/purge.h: $(OBJDIR)/headers $(OBJDIR)/rebuild_.c: $(SRCDIR)/rebuild.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/rebuild.c >$@ $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h: $(OBJDIR)/headers $(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/regexp.c >$@ $(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h: $(OBJDIR)/headers $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/report.c >$@ $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c $(OBJDIR)/report.h: $(OBJDIR)/headers $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/rss.c >$@ $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c $(OBJDIR)/rss.h: $(OBJDIR)/headers $(OBJDIR)/schema_.c: $(SRCDIR)/schema.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/schema.c >$@ $(OBJDIR)/schema.o: $(OBJDIR)/schema_.c $(OBJDIR)/schema.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/schema.o -c $(OBJDIR)/schema_.c $(OBJDIR)/schema.h: $(OBJDIR)/headers $(OBJDIR)/search_.c: $(SRCDIR)/search.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/search.c >$@ $(OBJDIR)/search.o: $(OBJDIR)/search_.c $(OBJDIR)/search.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/search.o -c $(OBJDIR)/search_.c $(OBJDIR)/search.h: $(OBJDIR)/headers $(OBJDIR)/setup_.c: $(SRCDIR)/setup.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/setup.c >$@ $(OBJDIR)/setup.o: $(OBJDIR)/setup_.c $(OBJDIR)/setup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c $(OBJDIR)/setup.h: $(OBJDIR)/headers $(OBJDIR)/sha1_.c: $(SRCDIR)/sha1.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sha1.c >$@ $(OBJDIR)/sha1.o: $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h: $(OBJDIR)/headers $(OBJDIR)/shun_.c: $(SRCDIR)/shun.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/shun.c >$@ $(OBJDIR)/shun.o: $(OBJDIR)/shun_.c $(OBJDIR)/shun.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/shun.o -c $(OBJDIR)/shun_.c $(OBJDIR)/shun.h: $(OBJDIR)/headers $(OBJDIR)/sitemap_.c: $(SRCDIR)/sitemap.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sitemap.c >$@ $(OBJDIR)/sitemap.o: $(OBJDIR)/sitemap_.c $(OBJDIR)/sitemap.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sitemap.o -c $(OBJDIR)/sitemap_.c $(OBJDIR)/sitemap.h: $(OBJDIR)/headers $(OBJDIR)/skins_.c: $(SRCDIR)/skins.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/skins.c >$@ $(OBJDIR)/skins.o: $(OBJDIR)/skins_.c $(OBJDIR)/skins.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c $(OBJDIR)/skins.h: $(OBJDIR)/headers $(OBJDIR)/sqlcmd_.c: $(SRCDIR)/sqlcmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sqlcmd.c >$@ $(OBJDIR)/sqlcmd.o: $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h: $(OBJDIR)/headers $(OBJDIR)/stash_.c: $(SRCDIR)/stash.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/stash.c >$@ $(OBJDIR)/stash.o: $(OBJDIR)/stash_.c $(OBJDIR)/stash.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/stash.o -c $(OBJDIR)/stash_.c $(OBJDIR)/stash.h: $(OBJDIR)/headers $(OBJDIR)/stat_.c: $(SRCDIR)/stat.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/stat.c >$@ $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c $(OBJDIR)/stat.h: $(OBJDIR)/headers $(OBJDIR)/statrep_.c: $(SRCDIR)/statrep.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/statrep.c >$@ $(OBJDIR)/statrep.o: $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/statrep.o -c $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h: $(OBJDIR)/headers $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/style.c >$@ $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/style.o -c $(OBJDIR)/style_.c $(OBJDIR)/style.h: $(OBJDIR)/headers $(OBJDIR)/sync_.c: $(SRCDIR)/sync.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sync.c >$@ $(OBJDIR)/sync.o: $(OBJDIR)/sync_.c $(OBJDIR)/sync.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sync.o -c $(OBJDIR)/sync_.c $(OBJDIR)/sync.h: $(OBJDIR)/headers $(OBJDIR)/tag_.c: $(SRCDIR)/tag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/tag.c >$@ $(OBJDIR)/tag.o: $(OBJDIR)/tag_.c $(OBJDIR)/tag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tag.o -c $(OBJDIR)/tag_.c $(OBJDIR)/tag.h: $(OBJDIR)/headers $(OBJDIR)/tar_.c: $(SRCDIR)/tar.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/tar.c >$@ $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c $(OBJDIR)/tar.h: $(OBJDIR)/headers $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/th_main.c >$@ $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/th_main.o -c $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h: $(OBJDIR)/headers $(OBJDIR)/timeline_.c: $(SRCDIR)/timeline.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/timeline.c >$@ $(OBJDIR)/timeline.o: $(OBJDIR)/timeline_.c $(OBJDIR)/timeline.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/timeline.o -c $(OBJDIR)/timeline_.c $(OBJDIR)/timeline.h: $(OBJDIR)/headers $(OBJDIR)/tkt_.c: $(SRCDIR)/tkt.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/tkt.c >$@ $(OBJDIR)/tkt.o: $(OBJDIR)/tkt_.c $(OBJDIR)/tkt.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tkt.o -c $(OBJDIR)/tkt_.c $(OBJDIR)/tkt.h: $(OBJDIR)/headers $(OBJDIR)/tktsetup_.c: $(SRCDIR)/tktsetup.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/tktsetup.c >$@ $(OBJDIR)/tktsetup.o: $(OBJDIR)/tktsetup_.c $(OBJDIR)/tktsetup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tktsetup.o -c $(OBJDIR)/tktsetup_.c $(OBJDIR)/tktsetup.h: $(OBJDIR)/headers $(OBJDIR)/undo_.c: $(SRCDIR)/undo.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/undo.c >$@ $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c $(OBJDIR)/undo.h: $(OBJDIR)/headers $(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/unicode.c >$@ $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h: $(OBJDIR)/headers $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/update.c >$@ $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c $(OBJDIR)/update.h: $(OBJDIR)/headers $(OBJDIR)/url_.c: $(SRCDIR)/url.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/url.c >$@ $(OBJDIR)/url.o: $(OBJDIR)/url_.c $(OBJDIR)/url.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/url.o -c $(OBJDIR)/url_.c $(OBJDIR)/url.h: $(OBJDIR)/headers $(OBJDIR)/user_.c: $(SRCDIR)/user.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/user.c >$@ $(OBJDIR)/user.o: $(OBJDIR)/user_.c $(OBJDIR)/user.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/user.o -c $(OBJDIR)/user_.c $(OBJDIR)/user.h: $(OBJDIR)/headers $(OBJDIR)/utf8_.c: $(SRCDIR)/utf8.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/utf8.c >$@ $(OBJDIR)/utf8.o: $(OBJDIR)/utf8_.c $(OBJDIR)/utf8.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/utf8.o -c $(OBJDIR)/utf8_.c $(OBJDIR)/utf8.h: $(OBJDIR)/headers $(OBJDIR)/util_.c: $(SRCDIR)/util.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/util.c >$@ $(OBJDIR)/util.o: $(OBJDIR)/util_.c $(OBJDIR)/util.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/util.o -c $(OBJDIR)/util_.c $(OBJDIR)/util.h: $(OBJDIR)/headers $(OBJDIR)/verify_.c: $(SRCDIR)/verify.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/verify.c >$@ $(OBJDIR)/verify.o: $(OBJDIR)/verify_.c $(OBJDIR)/verify.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/verify.o -c $(OBJDIR)/verify_.c $(OBJDIR)/verify.h: $(OBJDIR)/headers $(OBJDIR)/vfile_.c: $(SRCDIR)/vfile.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/vfile.c >$@ $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h: $(OBJDIR)/headers $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wiki.c >$@ $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h: $(OBJDIR)/headers $(OBJDIR)/wikiformat_.c: $(SRCDIR)/wikiformat.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wikiformat.c >$@ $(OBJDIR)/wikiformat.o: $(OBJDIR)/wikiformat_.c $(OBJDIR)/wikiformat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wikiformat.o -c $(OBJDIR)/wikiformat_.c $(OBJDIR)/wikiformat.h: $(OBJDIR)/headers $(OBJDIR)/winfile_.c: $(SRCDIR)/winfile.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/winfile.c >$@ $(OBJDIR)/winfile.o: $(OBJDIR)/winfile_.c $(OBJDIR)/winfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/winfile.o -c $(OBJDIR)/winfile_.c $(OBJDIR)/winfile.h: $(OBJDIR)/headers $(OBJDIR)/winhttp_.c: $(SRCDIR)/winhttp.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/winhttp.c >$@ $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h: $(OBJDIR)/headers $(OBJDIR)/wysiwyg_.c: $(SRCDIR)/wysiwyg.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wysiwyg.c >$@ $(OBJDIR)/wysiwyg.o: $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h: $(OBJDIR)/headers $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/xfer.c >$@ $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h: $(OBJDIR)/headers $(OBJDIR)/xfersetup_.c: $(SRCDIR)/xfersetup.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/xfersetup.c >$@ $(OBJDIR)/xfersetup.o: $(OBJDIR)/xfersetup_.c $(OBJDIR)/xfersetup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/xfersetup.o -c $(OBJDIR)/xfersetup_.c $(OBJDIR)/xfersetup.h: $(OBJDIR)/headers $(OBJDIR)/zip_.c: $(SRCDIR)/zip.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/zip.c >$@ $(OBJDIR)/zip.o: $(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c $(OBJDIR)/zip.h: $(OBJDIR)/headers SQLITE_OPTIONS = -DNDEBUG=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DSQLITE_ENABLE_LOCKING_STYLE=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_FILE_FORMAT=4 \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_FTS3_PARENTHESIS \ -DSQLITE_WIN32_NO_ANSI \ -D_HAVE__MINGW_H \ -DSQLITE_USE_MALLOC_H \ -DSQLITE_USE_MSIZE SHELL_OPTIONS = -Dmain=sqlite3_shell \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ -DSQLITE_SHELL_DBNAME_PROC=fossil_open \ -Daccess=file_access \ -Dsystem=fossil_system \ -Dgetenv=fossil_getenv \ -Dfopen=fossil_fopen MINIZ_OPTIONS = -DMINIZ_NO_STDIO \ -DMINIZ_NO_TIME \ -DMINIZ_NO_ARCHIVE_APIS $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c $(SRCDIR)/../win/Makefile.mingw $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) -c $(SRCDIR)/sqlite3.c -o $@ $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $@ $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h $(SRCDIR)/../win/Makefile.mingw $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)/shell.c -o $@ $(OBJDIR)/th.o: $(SRCDIR)/th.c $(XTCC) -c $(SRCDIR)/th.c -o $@ $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c $(XTCC) -c $(SRCDIR)/th_lang.c -o $@ $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c $(XTCC) -c $(SRCDIR)/th_tcl.c -o $@ $(OBJDIR)/miniz.o: $(SRCDIR)/miniz.c $(XTCC) $(MINIZ_OPTIONS) -c $(SRCDIR)/miniz.c -o $@ |
Changes to win/Makefile.mingw.mistachkin.
︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #### Enable JSON (http://www.json.org) support using "cson" # FOSSIL_ENABLE_JSON = 1 #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # FOSSIL_ENABLE_TCL = 1 #### Load Tcl using the stubs library mechanism # FOSSIL_ENABLE_TCL_STUBS = 1 #### Load Tcl using the private stubs mechanism # FOSSIL_ENABLE_TCL_PRIVATE_STUBS = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### Check if the workaround for the MinGW command line handling needs to | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | #### Enable JSON (http://www.json.org) support using "cson" # FOSSIL_ENABLE_JSON = 1 #### Enable HTTPS support via OpenSSL (links to libssl and libcrypto) # FOSSIL_ENABLE_SSL = 1 #### Automatically build OpenSSL when building Fossil (causes rebuild # issues when building incrementally). # # FOSSIL_BUILD_SSL = 1 #### Enable TH1 scripts in embedded documentation files # FOSSIL_ENABLE_TH1_DOCS = 1 #### Enable hooks for commands and web pages via TH1 # FOSSIL_ENABLE_TH1_HOOKS = 1 #### Enable scripting support via Tcl/Tk # FOSSIL_ENABLE_TCL = 1 #### Load Tcl using the stubs library mechanism # FOSSIL_ENABLE_TCL_STUBS = 1 #### Load Tcl using the private stubs mechanism # FOSSIL_ENABLE_TCL_PRIVATE_STUBS = 1 #### Use 'system' SQLite # # USE_SYSTEM_SQLITE = 1 #### Use the miniz compression library # # FOSSIL_ENABLE_MINIZ = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 #### Check if the workaround for the MinGW command line handling needs to # be enabled by default. This check may be somewhat fragile due to the # use of "findstring". # ifndef MINGW_IS_32BIT_ONLY ifeq (,$(findstring w64-mingw32,$(PREFIX))) MINGW_IS_32BIT_ONLY = 1 endif endif #### The directories where the zlib include and library files are located. # ZINCDIR = $(SRCDIR)/../compat/zlib ZLIBDIR = $(SRCDIR)/../compat/zlib #### Make an attempt to detect if Fossil is being built for the x64 processor # architecture. This check may be somewhat fragile due to "findstring". # ifndef X64 ifneq (,$(findstring x86_64-w64-mingw32,$(PREFIX))) X64 = 1 endif endif #### Determine if the optimized assembly routines provided with zlib should be # used, taking into account whether zlib is actually enabled and the target # processor architecture. # ifndef X64 SSLCONFIG = mingw ifndef FOSSIL_ENABLE_MINIZ ZLIBCONFIG = LOC="-DASMV -DASMINF" OBJA="inffas86.o match.o" LIBTARGETS = $(ZLIBDIR)/inffas86.o $(ZLIBDIR)/match.o else ZLIBCONFIG = LIBTARGETS = endif else SSLCONFIG = mingw64 ZLIBCONFIG = LIBTARGETS = endif #### Disable creation of the OpenSSL shared libraries. Also, disable support # for both SSLv2 and SSLv3 (i.e. thereby forcing the use of TLS). # SSLCONFIG += no-ssl2 no-ssl3 no-shared #### When using zlib, make sure that OpenSSL is configured to use the zlib # that Fossil knows about (i.e. the one within the source tree). # ifndef FOSSIL_ENABLE_MINIZ SSLCONFIG += --with-zlib-lib=$(PWD)/$(ZLIBDIR) --with-zlib-include=$(PWD)/$(ZLIBDIR) zlib endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2a OPENSSLINCDIR = $(OPENSSLDIR)/include OPENSSLLIBDIR = $(OPENSSLDIR) #### Either the directory where the Tcl library is installed or the Tcl # source code directory resides (depending on the value of the macro # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, # this directory must have "include" and "lib" sub-directories. If # this points to the Tcl source code directory, this directory must # have "generic" and "win" sub-directories. The recommended usage |
︙ | ︙ | |||
128 129 130 131 132 133 134 | #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # | | > > > > > > | > > > > > > > > > > | | > > > > > > > > > > > > | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | #### C Compile and options for use in building executables that # will run on the target platform. This is usually the same # as BCC, unless you are cross-compiling. This C compiler builds # the finished binary for fossil. The BCC compiler above is used # for building intermediate code-generator tools. # TCC = $(PREFIX)gcc -Os -Wall #### When not using the miniz compression library, zlib is required. # ifndef FOSSIL_ENABLE_MINIZ TCC += -L$(ZLIBDIR) -I$(ZINCDIR) endif #### Add the necessary command line options to build with debugging # symbols, if enabled. # ifdef FOSSIL_ENABLE_SYMBOLS TCC += -g endif #### Compile resources for use in building executables that will run # on the target platform. # RCC = $(PREFIX)windres -I$(SRCDIR) ifndef FOSSIL_ENABLE_MINIZ RCC += -I$(ZINCDIR) endif # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -L$(OPENSSLLIBDIR) -I$(OPENSSLINCDIR) RCC += -I$(OPENSSLINCDIR) endif # With Tcl support ifdef FOSSIL_ENABLE_TCL ifdef FOSSIL_TCL_SOURCE TCC += -L$(TCLSRCDIR)/win -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win RCC += -I$(TCLSRCDIR)/generic -I$(TCLSRCDIR)/win else TCC += -L$(TCLLIBDIR) -I$(TCLINCDIR) RCC += -I$(TCLINCDIR) endif endif # With miniz (i.e. instead of zlib) ifdef FOSSIL_ENABLE_MINIZ TCC += -DFOSSIL_ENABLE_MINIZ=1 RCC += -DFOSSIL_ENABLE_MINIZ=1 endif # With MinGW command line handling workaround ifdef MINGW_IS_32BIT_ONLY TCC += -DBROKEN_MINGW_CMDLINE=1 RCC += -DBROKEN_MINGW_CMDLINE=1 endif # With HTTPS support ifdef FOSSIL_ENABLE_SSL TCC += -DFOSSIL_ENABLE_SSL=1 RCC += -DFOSSIL_ENABLE_SSL=1 endif # With TH1 embedded docs support ifdef FOSSIL_ENABLE_TH1_DOCS TCC += -DFOSSIL_ENABLE_TH1_DOCS=1 RCC += -DFOSSIL_ENABLE_TH1_DOCS=1 endif # With TH1 hook support ifdef FOSSIL_ENABLE_TH1_HOOKS TCC += -DFOSSIL_ENABLE_TH1_HOOKS=1 RCC += -DFOSSIL_ENABLE_TH1_HOOKS=1 endif # With Tcl support ifdef FOSSIL_ENABLE_TCL TCC += -DFOSSIL_ENABLE_TCL=1 RCC += -DFOSSIL_ENABLE_TCL=1 # Either statically linked or via stubs ifdef FOSSIL_ENABLE_TCL_STUBS |
︙ | ︙ | |||
200 201 202 203 204 205 206 | endif #### We add the -static option here so that we can build a static # executable that will run in a chroot jail. # LIB = -static | | > > > > > > > | > | > | > > > > > > | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 | endif #### We add the -static option here so that we can build a static # executable that will run in a chroot jail. # LIB = -static #### MinGW: If available, use the Unicode capable runtime startup code. # ifndef MINGW_IS_32BIT_ONLY LIB += -municode endif #### SQLite: If enabled, use the system SQLite library. # ifdef USE_SYSTEM_SQLITE LIB += -lsqlite3 endif #### OpenSSL: Add the necessary libraries required, if enabled. # ifdef FOSSIL_ENABLE_SSL LIB += -lssl -lcrypto -lgdi32 endif #### Tcl: Add the necessary libraries required, if enabled. # ifdef FOSSIL_ENABLE_TCL LIB += $(LIBTCL) endif #### Extra arguments for linking the finished binary. Fossil needs # to link against the Z-Lib compression library. There are no # other mandatory dependencies. # LIB += -lmingwex #### When not using the miniz compression library, zlib is required. # ifndef FOSSIL_ENABLE_MINIZ LIB += -lz endif #### These libraries MUST appear in the same order as they do for Tcl # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL ifdef FOSSIL_ENABLE_TCL_STUBS LIB += -lkernel32 -lws2_32 |
︙ | ︙ | |||
241 242 243 244 245 246 247 | #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location # | | > > > > > > > | 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # TCLSH = tclsh #### Nullsoft installer MakeNSIS location # MAKENSIS = "$(PROGRAMFILES)\NSIS\MakeNSIS.exe" #### Inno Setup executable location # INNOSETUP = "$(PROGRAMFILES)\Inno Setup 5\ISCC.exe" #### Include a configuration file that can override any one of these settings. # -include config.w32 # STOP HERE # You should not need to change anything below this line #-------------------------------------------------------- XTCC = $(TCC) $(CFLAGS) -I. -I$(SRCDIR) SRC = \ $(SRCDIR)/add.c \ $(SRCDIR)/allrepo.c \ $(SRCDIR)/attach.c \ $(SRCDIR)/bag.c \ $(SRCDIR)/bisect.c \ $(SRCDIR)/blob.c \ $(SRCDIR)/branch.c \ $(SRCDIR)/browse.c \ $(SRCDIR)/builtin.c \ $(SRCDIR)/bundle.c \ $(SRCDIR)/cache.c \ $(SRCDIR)/captcha.c \ $(SRCDIR)/cgi.c \ $(SRCDIR)/checkin.c \ $(SRCDIR)/checkout.c \ $(SRCDIR)/clearsign.c \ $(SRCDIR)/clone.c \ $(SRCDIR)/comformat.c \ |
︙ | ︙ | |||
282 283 284 285 286 287 288 289 290 291 292 293 294 295 | $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ | > > | 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ $(SRCDIR)/export.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/foci.c \ $(SRCDIR)/fusefs.c \ $(SRCDIR)/glob.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/gzip.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ |
︙ | ︙ | |||
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | $(SRCDIR)/json_report.c \ $(SRCDIR)/json_status.c \ $(SRCDIR)/json_tag.c \ $(SRCDIR)/json_timeline.c \ $(SRCDIR)/json_user.c \ $(SRCDIR)/json_wiki.c \ $(SRCDIR)/leaf.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/path.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ $(SRCDIR)/tar.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ $(SRCDIR)/unicode.c \ $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | $(SRCDIR)/json_report.c \ $(SRCDIR)/json_status.c \ $(SRCDIR)/json_tag.c \ $(SRCDIR)/json_timeline.c \ $(SRCDIR)/json_user.c \ $(SRCDIR)/json_wiki.c \ $(SRCDIR)/leaf.c \ $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/path.c \ $(SRCDIR)/pivot.c \ $(SRCDIR)/popen.c \ $(SRCDIR)/pqueue.c \ $(SRCDIR)/printf.c \ $(SRCDIR)/publish.c \ $(SRCDIR)/purge.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/sha1.c \ $(SRCDIR)/shun.c \ $(SRCDIR)/sitemap.c \ $(SRCDIR)/skins.c \ $(SRCDIR)/sqlcmd.c \ $(SRCDIR)/stash.c \ $(SRCDIR)/stat.c \ $(SRCDIR)/statrep.c \ $(SRCDIR)/style.c \ $(SRCDIR)/sync.c \ $(SRCDIR)/tag.c \ $(SRCDIR)/tar.c \ $(SRCDIR)/th_main.c \ $(SRCDIR)/timeline.c \ $(SRCDIR)/tkt.c \ $(SRCDIR)/tktsetup.c \ $(SRCDIR)/undo.c \ $(SRCDIR)/unicode.c \ $(SRCDIR)/update.c \ $(SRCDIR)/url.c \ $(SRCDIR)/user.c \ $(SRCDIR)/utf8.c \ $(SRCDIR)/util.c \ $(SRCDIR)/verify.c \ $(SRCDIR)/vfile.c \ $(SRCDIR)/wiki.c \ $(SRCDIR)/wikiformat.c \ $(SRCDIR)/winfile.c \ $(SRCDIR)/winhttp.c \ $(SRCDIR)/wysiwyg.c \ $(SRCDIR)/xfer.c \ $(SRCDIR)/xfersetup.c \ $(SRCDIR)/zip.c EXTRA_FILES = \ $(SRCDIR)/../skins/aht/details.txt \ $(SRCDIR)/../skins/black_and_white/css.txt \ $(SRCDIR)/../skins/black_and_white/details.txt \ $(SRCDIR)/../skins/black_and_white/footer.txt \ $(SRCDIR)/../skins/black_and_white/header.txt \ $(SRCDIR)/../skins/blitz/css.txt \ $(SRCDIR)/../skins/blitz/details.txt \ $(SRCDIR)/../skins/blitz/footer.txt \ $(SRCDIR)/../skins/blitz/header.txt \ $(SRCDIR)/../skins/blitz/ticket.txt \ $(SRCDIR)/../skins/blitz_no_logo/css.txt \ $(SRCDIR)/../skins/blitz_no_logo/details.txt \ $(SRCDIR)/../skins/blitz_no_logo/footer.txt \ $(SRCDIR)/../skins/blitz_no_logo/header.txt \ $(SRCDIR)/../skins/blitz_no_logo/ticket.txt \ $(SRCDIR)/../skins/default/css.txt \ $(SRCDIR)/../skins/default/details.txt \ $(SRCDIR)/../skins/default/footer.txt \ $(SRCDIR)/../skins/default/header.txt \ $(SRCDIR)/../skins/eagle/css.txt \ $(SRCDIR)/../skins/eagle/details.txt \ $(SRCDIR)/../skins/eagle/footer.txt \ $(SRCDIR)/../skins/eagle/header.txt \ $(SRCDIR)/../skins/enhanced1/css.txt \ $(SRCDIR)/../skins/enhanced1/details.txt \ $(SRCDIR)/../skins/enhanced1/footer.txt \ $(SRCDIR)/../skins/enhanced1/header.txt \ $(SRCDIR)/../skins/khaki/css.txt \ $(SRCDIR)/../skins/khaki/details.txt \ $(SRCDIR)/../skins/khaki/footer.txt \ $(SRCDIR)/../skins/khaki/header.txt \ $(SRCDIR)/../skins/original/css.txt \ $(SRCDIR)/../skins/original/details.txt \ $(SRCDIR)/../skins/original/footer.txt \ $(SRCDIR)/../skins/original/header.txt \ $(SRCDIR)/../skins/plain_gray/css.txt \ $(SRCDIR)/../skins/plain_gray/details.txt \ $(SRCDIR)/../skins/plain_gray/footer.txt \ $(SRCDIR)/../skins/plain_gray/header.txt \ $(SRCDIR)/../skins/rounded1/css.txt \ $(SRCDIR)/../skins/rounded1/details.txt \ $(SRCDIR)/../skins/rounded1/footer.txt \ $(SRCDIR)/../skins/rounded1/header.txt \ $(SRCDIR)/../skins/xekri/css.txt \ $(SRCDIR)/../skins/xekri/details.txt \ $(SRCDIR)/../skins/xekri/footer.txt \ $(SRCDIR)/../skins/xekri/header.txt \ $(SRCDIR)/diff.tcl \ $(SRCDIR)/markdown.md TRANS_SRC = \ $(OBJDIR)/add_.c \ $(OBJDIR)/allrepo_.c \ $(OBJDIR)/attach_.c \ $(OBJDIR)/bag_.c \ $(OBJDIR)/bisect_.c \ $(OBJDIR)/blob_.c \ $(OBJDIR)/branch_.c \ $(OBJDIR)/browse_.c \ $(OBJDIR)/builtin_.c \ $(OBJDIR)/bundle_.c \ $(OBJDIR)/cache_.c \ $(OBJDIR)/captcha_.c \ $(OBJDIR)/cgi_.c \ $(OBJDIR)/checkin_.c \ $(OBJDIR)/checkout_.c \ $(OBJDIR)/clearsign_.c \ $(OBJDIR)/clone_.c \ $(OBJDIR)/comformat_.c \ |
︙ | ︙ | |||
391 392 393 394 395 396 397 398 399 400 401 402 403 404 | $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ | > > | 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/event_.c \ $(OBJDIR)/export_.c \ $(OBJDIR)/file_.c \ $(OBJDIR)/finfo_.c \ $(OBJDIR)/foci_.c \ $(OBJDIR)/fusefs_.c \ $(OBJDIR)/glob_.c \ $(OBJDIR)/graph_.c \ $(OBJDIR)/gzip_.c \ $(OBJDIR)/http_.c \ $(OBJDIR)/http_socket_.c \ $(OBJDIR)/http_ssl_.c \ $(OBJDIR)/http_transport_.c \ |
︙ | ︙ | |||
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 | $(OBJDIR)/json_report_.c \ $(OBJDIR)/json_status_.c \ $(OBJDIR)/json_tag_.c \ $(OBJDIR)/json_timeline_.c \ $(OBJDIR)/json_user_.c \ $(OBJDIR)/json_wiki_.c \ $(OBJDIR)/leaf_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/pivot_.c \ $(OBJDIR)/popen_.c \ $(OBJDIR)/pqueue_.c \ $(OBJDIR)/printf_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ $(OBJDIR)/tar_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ $(OBJDIR)/unicode_.c \ $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ | > > > > > > > > > | 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 | $(OBJDIR)/json_report_.c \ $(OBJDIR)/json_status_.c \ $(OBJDIR)/json_tag_.c \ $(OBJDIR)/json_timeline_.c \ $(OBJDIR)/json_user_.c \ $(OBJDIR)/json_wiki_.c \ $(OBJDIR)/leaf_.c \ $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/path_.c \ $(OBJDIR)/pivot_.c \ $(OBJDIR)/popen_.c \ $(OBJDIR)/pqueue_.c \ $(OBJDIR)/printf_.c \ $(OBJDIR)/publish_.c \ $(OBJDIR)/purge_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/sha1_.c \ $(OBJDIR)/shun_.c \ $(OBJDIR)/sitemap_.c \ $(OBJDIR)/skins_.c \ $(OBJDIR)/sqlcmd_.c \ $(OBJDIR)/stash_.c \ $(OBJDIR)/stat_.c \ $(OBJDIR)/statrep_.c \ $(OBJDIR)/style_.c \ $(OBJDIR)/sync_.c \ $(OBJDIR)/tag_.c \ $(OBJDIR)/tar_.c \ $(OBJDIR)/th_main_.c \ $(OBJDIR)/timeline_.c \ $(OBJDIR)/tkt_.c \ $(OBJDIR)/tktsetup_.c \ $(OBJDIR)/undo_.c \ $(OBJDIR)/unicode_.c \ $(OBJDIR)/update_.c \ $(OBJDIR)/url_.c \ $(OBJDIR)/user_.c \ $(OBJDIR)/utf8_.c \ $(OBJDIR)/util_.c \ $(OBJDIR)/verify_.c \ $(OBJDIR)/vfile_.c \ $(OBJDIR)/wiki_.c \ $(OBJDIR)/wikiformat_.c \ $(OBJDIR)/winfile_.c \ $(OBJDIR)/winhttp_.c \ $(OBJDIR)/wysiwyg_.c \ $(OBJDIR)/xfer_.c \ $(OBJDIR)/xfersetup_.c \ $(OBJDIR)/zip_.c OBJ = \ $(OBJDIR)/add.o \ $(OBJDIR)/allrepo.o \ $(OBJDIR)/attach.o \ $(OBJDIR)/bag.o \ $(OBJDIR)/bisect.o \ $(OBJDIR)/blob.o \ $(OBJDIR)/branch.o \ $(OBJDIR)/browse.o \ $(OBJDIR)/builtin.o \ $(OBJDIR)/bundle.o \ $(OBJDIR)/cache.o \ $(OBJDIR)/captcha.o \ $(OBJDIR)/cgi.o \ $(OBJDIR)/checkin.o \ $(OBJDIR)/checkout.o \ $(OBJDIR)/clearsign.o \ $(OBJDIR)/clone.o \ $(OBJDIR)/comformat.o \ |
︙ | ︙ | |||
500 501 502 503 504 505 506 507 508 509 510 511 512 513 | $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ | > > | 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 | $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ $(OBJDIR)/export.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/foci.o \ $(OBJDIR)/fusefs.o \ $(OBJDIR)/glob.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/gzip.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ |
︙ | ︙ | |||
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 | $(OBJDIR)/json_report.o \ $(OBJDIR)/json_status.o \ $(OBJDIR)/json_tag.o \ $(OBJDIR)/json_timeline.o \ $(OBJDIR)/json_user.o \ $(OBJDIR)/json_wiki.o \ $(OBJDIR)/leaf.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/path.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ $(OBJDIR)/tar.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ $(OBJDIR)/unicode.o \ $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o | > > > > > > | > | | | | > > > > | | | | > > > > > > > > | | | | | | > > > | | > > > | | > > > > > > > | > > > > > > > > > | > > > | > | | > > > > > > > > > > > | | > | > > > > > > | | > > > | > > > | > > > | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 | $(OBJDIR)/json_report.o \ $(OBJDIR)/json_status.o \ $(OBJDIR)/json_tag.o \ $(OBJDIR)/json_timeline.o \ $(OBJDIR)/json_user.o \ $(OBJDIR)/json_wiki.o \ $(OBJDIR)/leaf.o \ $(OBJDIR)/loadctrl.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/path.o \ $(OBJDIR)/pivot.o \ $(OBJDIR)/popen.o \ $(OBJDIR)/pqueue.o \ $(OBJDIR)/printf.o \ $(OBJDIR)/publish.o \ $(OBJDIR)/purge.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/sha1.o \ $(OBJDIR)/shun.o \ $(OBJDIR)/sitemap.o \ $(OBJDIR)/skins.o \ $(OBJDIR)/sqlcmd.o \ $(OBJDIR)/stash.o \ $(OBJDIR)/stat.o \ $(OBJDIR)/statrep.o \ $(OBJDIR)/style.o \ $(OBJDIR)/sync.o \ $(OBJDIR)/tag.o \ $(OBJDIR)/tar.o \ $(OBJDIR)/th_main.o \ $(OBJDIR)/timeline.o \ $(OBJDIR)/tkt.o \ $(OBJDIR)/tktsetup.o \ $(OBJDIR)/undo.o \ $(OBJDIR)/unicode.o \ $(OBJDIR)/update.o \ $(OBJDIR)/url.o \ $(OBJDIR)/user.o \ $(OBJDIR)/utf8.o \ $(OBJDIR)/util.o \ $(OBJDIR)/verify.o \ $(OBJDIR)/vfile.o \ $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/wysiwyg.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o APPNAME = fossil.exe APPTARGETS = #### If the USE_WINDOWS variable exists, it is assumed that we are building # inside of a Windows-style shell; otherwise, it is assumed that we are # building inside of a Unix-style shell. Note that the "move" command is # broken when attempting to use it from the Windows shell via MinGW make # because the SHELL variable is only used for certain commands that are # recognized internally by make. # ifdef USE_WINDOWS TRANSLATE = $(subst /,\,$(OBJDIR)/translate.exe) MAKEHEADERS = $(subst /,\,$(OBJDIR)/makeheaders.exe) MKINDEX = $(subst /,\,$(OBJDIR)/mkindex.exe) MKBUILTIN = $(subst /,\,$(OBJDIR)/mkbuiltin.exe) MKVERSION = $(subst /,\,$(OBJDIR)/mkversion.exe) CODECHECK1 = $(subst /,\,$(OBJDIR)/codecheck1.exe) CAT = type CP = copy GREP = find MV = copy RM = del /Q MKDIR = -mkdir RMDIR = rmdir /S /Q else TRANSLATE = $(OBJDIR)/translate.exe MAKEHEADERS = $(OBJDIR)/makeheaders.exe MKINDEX = $(OBJDIR)/mkindex.exe MKBUILTIN = $(OBJDIR)/mkbuiltin.exe MKVERSION = $(OBJDIR)/mkversion.exe CODECHECK1 = $(OBJDIR)/codecheck1.exe CAT = cat CP = cp GREP = grep MV = mv RM = rm -f MKDIR = -mkdir -p RMDIR = rm -rf endif all: $(OBJDIR) $(APPNAME) $(OBJDIR)/fossil.o: $(SRCDIR)/../win/fossil.rc $(OBJDIR)/VERSION.h ifdef USE_WINDOWS $(CAT) $(subst /,\,$(SRCDIR)\miniz.c) | $(GREP) "define MZ_VERSION" > $(subst /,\,$(OBJDIR)\minizver.h) $(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.rc) $(subst /,\,$(OBJDIR)) $(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.ico) $(subst /,\,$(OBJDIR)) $(CP) $(subst /,\,$(SRCDIR)\..\win\fossil.exe.manifest) $(subst /,\,$(OBJDIR)) else $(CAT) $(SRCDIR)/miniz.c | $(GREP) "define MZ_VERSION" > $(OBJDIR)/minizver.h $(CP) $(SRCDIR)/../win/fossil.rc $(OBJDIR) $(CP) $(SRCDIR)/../win/fossil.ico $(OBJDIR) $(CP) $(SRCDIR)/../win/fossil.exe.manifest $(OBJDIR) endif $(RCC) $(OBJDIR)/fossil.rc -o $(OBJDIR)/fossil.o install: $(OBJDIR) $(APPNAME) ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(INSTALLDIR)) $(MV) $(subst /,\,$(APPNAME)) $(subst /,\,$(INSTALLDIR)) else $(MKDIR) $(INSTALLDIR) $(MV) $(APPNAME) $(INSTALLDIR) endif $(OBJDIR): ifdef USE_WINDOWS $(MKDIR) $(subst /,\,$(OBJDIR)) else $(MKDIR) $(OBJDIR) endif $(TRANSLATE): $(SRCDIR)/translate.c $(BCC) -o $@ $(SRCDIR)/translate.c $(MAKEHEADERS): $(SRCDIR)/makeheaders.c $(BCC) -o $@ $(SRCDIR)/makeheaders.c $(MKINDEX): $(SRCDIR)/mkindex.c $(BCC) -o $@ $(SRCDIR)/mkindex.c $(MKBUILTIN): $(SRCDIR)/mkbuiltin.c $(BCC) -o $@ $(SRCDIR)/mkbuiltin.c $(MKVERSION): $(SRCDIR)/mkversion.c $(BCC) -o $@ $(SRCDIR)/mkversion.c $(CODECHECK1): $(SRCDIR)/codecheck1.c $(BCC) -o $@ $(SRCDIR)/codecheck1.c # WARNING. DANGER. Running the test suite modifies the repository the # build is done from, i.e. the checkout belongs to. Do not sync/push # the repository after running the tests. test: $(OBJDIR) $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(MKVERSION) $(MKVERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$@ # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set # to 1. If it is set to 1, then there is no need to build or link # the sqlite3.o object. Instead, the system SQLite will be linked # using -lsqlite3. SQLITE3_OBJ.1 = SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o SQLITE3_OBJ. = $(SQLITE3_OBJ.0) # The FOSSIL_ENABLE_MINIZ variable may be undefined, set to 0, or # set to 1. If it is set to 1, the miniz library included in the # source tree should be used; otherwise, it should not. MINIZ_OBJ.0 = MINIZ_OBJ.1 = $(OBJDIR)/miniz.o MINIZ_OBJ. = $(MINIZ_OBJ.0) EXTRAOBJ = \ $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \ $(MINIZ_OBJ.$(FOSSIL_ENABLE_MINIZ)) \ $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ $(OBJDIR)/th_lang.o \ $(OBJDIR)/th_tcl.o \ $(OBJDIR)/cson_amalgamation.o zlib: $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) $(ZLIBCONFIG) -f win32/Makefile.gcc libz.a clean-zlib: $(MAKE) -C $(ZLIBDIR) PREFIX=$(PREFIX) -f win32/Makefile.gcc clean $(ZLIBDIR)/inffas86.o: $(TCC) -c -o $@ -DASMINF -I$(ZLIBDIR) -O3 $(ZLIBDIR)/contrib/inflate86/inffas86.c $(ZLIBDIR)/match.o: $(TCC) -c -o $@ -DASMV $(ZLIBDIR)/contrib/asm686/match.S ifndef FOSSIL_ENABLE_MINIZ LIBTARGETS += zlib endif openssl: $(LIBTARGETS) cd $(OPENSSLLIBDIR);./Configure --cross-compile-prefix=$(PREFIX) $(SSLCONFIG) $(MAKE) -C $(OPENSSLLIBDIR) build_libs clean-openssl: $(MAKE) -C $(OPENSSLLIBDIR) clean tcl: cd $(TCLSRCDIR)/win;./configure $(MAKE) -C $(TCLSRCDIR)/win $(TCLTARGET) clean-tcl: $(MAKE) -C $(TCLSRCDIR)/win distclean APPTARGETS += $(LIBTARGETS) ifdef FOSSIL_BUILD_SSL APPTARGETS += openssl endif $(APPNAME): $(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(CODECHECK1) $(TRANS_SRC) $(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/fossil.o # This rule prevents make from using its default rules to try build # an executable named "manifest" out of the file named "manifest.c" # $(SRCDIR)/../manifest: # noop clean: ifdef USE_WINDOWS $(RM) $(subst /,\,$(APPNAME)) $(RMDIR) $(subst /,\,$(OBJDIR)) else $(RM) $(APPNAME) $(RMDIR) $(OBJDIR) endif setup: $(OBJDIR) $(APPNAME) $(MAKENSIS) ./setup/fossil.nsi innosetup: $(OBJDIR) $(APPNAME) $(INNOSETUP) ./setup/fossil.iss -DAppVersion=$(shell $(CAT) ./VERSION) $(OBJDIR)/page_index.h: $(TRANS_SRC) $(MKINDEX) $(MKINDEX) $(TRANS_SRC) >$@ $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \ $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \ $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \ $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \ $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \ $(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \ $(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \ $(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \ $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \ $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \ $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \ $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \ $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \ $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \ $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ |
︙ | ︙ | |||
739 740 741 742 743 744 745 746 747 748 749 750 751 752 | $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \ $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \ $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \ | > > | 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 | $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/event_.c:$(OBJDIR)/event.h \ $(OBJDIR)/export_.c:$(OBJDIR)/export.h \ $(OBJDIR)/file_.c:$(OBJDIR)/file.h \ $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h \ $(OBJDIR)/foci_.c:$(OBJDIR)/foci.h \ $(OBJDIR)/fusefs_.c:$(OBJDIR)/fusefs.h \ $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h \ $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h \ $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h \ $(OBJDIR)/http_.c:$(OBJDIR)/http.h \ $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h \ $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h \ $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h \ |
︙ | ︙ | |||
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 | $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h \ $(OBJDIR)/json_status_.c:$(OBJDIR)/json_status.h \ $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h \ $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h \ $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h \ $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h \ $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ $(SRCDIR)/sqlite3.h \ $(SRCDIR)/th.h \ $(OBJDIR)/VERSION.h echo Done >$(OBJDIR)/headers $(OBJDIR)/headers: Makefile Makefile: | > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > | | | | | | | | | | | | > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | < | | > > > | 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 | $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h \ $(OBJDIR)/json_status_.c:$(OBJDIR)/json_status.h \ $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h \ $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h \ $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h \ $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h \ $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h \ $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h \ $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h \ $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h \ $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ $(OBJDIR)/publish_.c:$(OBJDIR)/publish.h \ $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \ $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h \ $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \ $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \ $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \ $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \ $(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \ $(OBJDIR)/update_.c:$(OBJDIR)/update.h \ $(OBJDIR)/url_.c:$(OBJDIR)/url.h \ $(OBJDIR)/user_.c:$(OBJDIR)/user.h \ $(OBJDIR)/utf8_.c:$(OBJDIR)/utf8.h \ $(OBJDIR)/util_.c:$(OBJDIR)/util.h \ $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h \ $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h \ $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h \ $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h \ $(OBJDIR)/winfile_.c:$(OBJDIR)/winfile.h \ $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h \ $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h \ $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h \ $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h \ $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h \ $(SRCDIR)/sqlite3.h \ $(SRCDIR)/th.h \ $(OBJDIR)/VERSION.h echo Done >$(OBJDIR)/headers $(OBJDIR)/headers: Makefile Makefile: $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/add.c >$@ $(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/add.o -c $(OBJDIR)/add_.c $(OBJDIR)/add.h: $(OBJDIR)/headers $(OBJDIR)/allrepo_.c: $(SRCDIR)/allrepo.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/allrepo.c >$@ $(OBJDIR)/allrepo.o: $(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/allrepo.o -c $(OBJDIR)/allrepo_.c $(OBJDIR)/allrepo.h: $(OBJDIR)/headers $(OBJDIR)/attach_.c: $(SRCDIR)/attach.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/attach.c >$@ $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c $(OBJDIR)/attach.h: $(OBJDIR)/headers $(OBJDIR)/bag_.c: $(SRCDIR)/bag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bag.c >$@ $(OBJDIR)/bag.o: $(OBJDIR)/bag_.c $(OBJDIR)/bag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bag.o -c $(OBJDIR)/bag_.c $(OBJDIR)/bag.h: $(OBJDIR)/headers $(OBJDIR)/bisect_.c: $(SRCDIR)/bisect.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bisect.c >$@ $(OBJDIR)/bisect.o: $(OBJDIR)/bisect_.c $(OBJDIR)/bisect.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bisect.o -c $(OBJDIR)/bisect_.c $(OBJDIR)/bisect.h: $(OBJDIR)/headers $(OBJDIR)/blob_.c: $(SRCDIR)/blob.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/blob.c >$@ $(OBJDIR)/blob.o: $(OBJDIR)/blob_.c $(OBJDIR)/blob.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/blob.o -c $(OBJDIR)/blob_.c $(OBJDIR)/blob.h: $(OBJDIR)/headers $(OBJDIR)/branch_.c: $(SRCDIR)/branch.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/branch.c >$@ $(OBJDIR)/branch.o: $(OBJDIR)/branch_.c $(OBJDIR)/branch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/branch.o -c $(OBJDIR)/branch_.c $(OBJDIR)/branch.h: $(OBJDIR)/headers $(OBJDIR)/browse_.c: $(SRCDIR)/browse.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/browse.c >$@ $(OBJDIR)/browse.o: $(OBJDIR)/browse_.c $(OBJDIR)/browse.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/browse.o -c $(OBJDIR)/browse_.c $(OBJDIR)/browse.h: $(OBJDIR)/headers $(OBJDIR)/builtin_.c: $(SRCDIR)/builtin.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/builtin.c >$@ $(OBJDIR)/builtin.o: $(OBJDIR)/builtin_.c $(OBJDIR)/builtin.h $(OBJDIR)/builtin_data.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/builtin.o -c $(OBJDIR)/builtin_.c $(OBJDIR)/builtin.h: $(OBJDIR)/headers $(OBJDIR)/bundle_.c: $(SRCDIR)/bundle.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/bundle.c >$@ $(OBJDIR)/bundle.o: $(OBJDIR)/bundle_.c $(OBJDIR)/bundle.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/bundle.o -c $(OBJDIR)/bundle_.c $(OBJDIR)/bundle.h: $(OBJDIR)/headers $(OBJDIR)/cache_.c: $(SRCDIR)/cache.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cache.c >$@ $(OBJDIR)/cache.o: $(OBJDIR)/cache_.c $(OBJDIR)/cache.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cache.o -c $(OBJDIR)/cache_.c $(OBJDIR)/cache.h: $(OBJDIR)/headers $(OBJDIR)/captcha_.c: $(SRCDIR)/captcha.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/captcha.c >$@ $(OBJDIR)/captcha.o: $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/captcha.o -c $(OBJDIR)/captcha_.c $(OBJDIR)/captcha.h: $(OBJDIR)/headers $(OBJDIR)/cgi_.c: $(SRCDIR)/cgi.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/cgi.c >$@ $(OBJDIR)/cgi.o: $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h: $(OBJDIR)/headers $(OBJDIR)/checkin_.c: $(SRCDIR)/checkin.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/checkin.c >$@ $(OBJDIR)/checkin.o: $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h: $(OBJDIR)/headers $(OBJDIR)/checkout_.c: $(SRCDIR)/checkout.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/checkout.c >$@ $(OBJDIR)/checkout.o: $(OBJDIR)/checkout_.c $(OBJDIR)/checkout.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/checkout.o -c $(OBJDIR)/checkout_.c $(OBJDIR)/checkout.h: $(OBJDIR)/headers $(OBJDIR)/clearsign_.c: $(SRCDIR)/clearsign.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/clearsign.c >$@ $(OBJDIR)/clearsign.o: $(OBJDIR)/clearsign_.c $(OBJDIR)/clearsign.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/clearsign.o -c $(OBJDIR)/clearsign_.c $(OBJDIR)/clearsign.h: $(OBJDIR)/headers $(OBJDIR)/clone_.c: $(SRCDIR)/clone.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/clone.c >$@ $(OBJDIR)/clone.o: $(OBJDIR)/clone_.c $(OBJDIR)/clone.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/clone.o -c $(OBJDIR)/clone_.c $(OBJDIR)/clone.h: $(OBJDIR)/headers $(OBJDIR)/comformat_.c: $(SRCDIR)/comformat.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/comformat.c >$@ $(OBJDIR)/comformat.o: $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/comformat.o -c $(OBJDIR)/comformat_.c $(OBJDIR)/comformat.h: $(OBJDIR)/headers $(OBJDIR)/configure_.c: $(SRCDIR)/configure.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/configure.c >$@ $(OBJDIR)/configure.o: $(OBJDIR)/configure_.c $(OBJDIR)/configure.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/configure.o -c $(OBJDIR)/configure_.c $(OBJDIR)/configure.h: $(OBJDIR)/headers $(OBJDIR)/content_.c: $(SRCDIR)/content.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/content.c >$@ $(OBJDIR)/content.o: $(OBJDIR)/content_.c $(OBJDIR)/content.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/content.o -c $(OBJDIR)/content_.c $(OBJDIR)/content.h: $(OBJDIR)/headers $(OBJDIR)/db_.c: $(SRCDIR)/db.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/db.c >$@ $(OBJDIR)/db.o: $(OBJDIR)/db_.c $(OBJDIR)/db.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/db.o -c $(OBJDIR)/db_.c $(OBJDIR)/db.h: $(OBJDIR)/headers $(OBJDIR)/delta_.c: $(SRCDIR)/delta.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/delta.c >$@ $(OBJDIR)/delta.o: $(OBJDIR)/delta_.c $(OBJDIR)/delta.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/delta.o -c $(OBJDIR)/delta_.c $(OBJDIR)/delta.h: $(OBJDIR)/headers $(OBJDIR)/deltacmd_.c: $(SRCDIR)/deltacmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltacmd.c >$@ $(OBJDIR)/deltacmd.o: $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h: $(OBJDIR)/headers $(OBJDIR)/descendants_.c: $(SRCDIR)/descendants.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/descendants.c >$@ $(OBJDIR)/descendants.o: $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h: $(OBJDIR)/headers $(OBJDIR)/diff_.c: $(SRCDIR)/diff.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/diff.c >$@ $(OBJDIR)/diff.o: $(OBJDIR)/diff_.c $(OBJDIR)/diff.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/diff.o -c $(OBJDIR)/diff_.c $(OBJDIR)/diff.h: $(OBJDIR)/headers $(OBJDIR)/diffcmd_.c: $(SRCDIR)/diffcmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/diffcmd.c >$@ $(OBJDIR)/diffcmd.o: $(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/diffcmd.o -c $(OBJDIR)/diffcmd_.c $(OBJDIR)/diffcmd.h: $(OBJDIR)/headers $(OBJDIR)/doc_.c: $(SRCDIR)/doc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/doc.c >$@ $(OBJDIR)/doc.o: $(OBJDIR)/doc_.c $(OBJDIR)/doc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/doc.o -c $(OBJDIR)/doc_.c $(OBJDIR)/doc.h: $(OBJDIR)/headers $(OBJDIR)/encode_.c: $(SRCDIR)/encode.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/encode.c >$@ $(OBJDIR)/encode.o: $(OBJDIR)/encode_.c $(OBJDIR)/encode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/encode.o -c $(OBJDIR)/encode_.c $(OBJDIR)/encode.h: $(OBJDIR)/headers $(OBJDIR)/event_.c: $(SRCDIR)/event.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/event.c >$@ $(OBJDIR)/event.o: $(OBJDIR)/event_.c $(OBJDIR)/event.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/event.o -c $(OBJDIR)/event_.c $(OBJDIR)/event.h: $(OBJDIR)/headers $(OBJDIR)/export_.c: $(SRCDIR)/export.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/export.c >$@ $(OBJDIR)/export.o: $(OBJDIR)/export_.c $(OBJDIR)/export.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/export.o -c $(OBJDIR)/export_.c $(OBJDIR)/export.h: $(OBJDIR)/headers $(OBJDIR)/file_.c: $(SRCDIR)/file.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/file.c >$@ $(OBJDIR)/file.o: $(OBJDIR)/file_.c $(OBJDIR)/file.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/file.o -c $(OBJDIR)/file_.c $(OBJDIR)/file.h: $(OBJDIR)/headers $(OBJDIR)/finfo_.c: $(SRCDIR)/finfo.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/finfo.c >$@ $(OBJDIR)/finfo.o: $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/finfo.o -c $(OBJDIR)/finfo_.c $(OBJDIR)/finfo.h: $(OBJDIR)/headers $(OBJDIR)/foci_.c: $(SRCDIR)/foci.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/foci.c >$@ $(OBJDIR)/foci.o: $(OBJDIR)/foci_.c $(OBJDIR)/foci.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/foci.o -c $(OBJDIR)/foci_.c $(OBJDIR)/foci.h: $(OBJDIR)/headers $(OBJDIR)/fusefs_.c: $(SRCDIR)/fusefs.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/fusefs.c >$@ $(OBJDIR)/fusefs.o: $(OBJDIR)/fusefs_.c $(OBJDIR)/fusefs.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/fusefs.o -c $(OBJDIR)/fusefs_.c $(OBJDIR)/fusefs.h: $(OBJDIR)/headers $(OBJDIR)/glob_.c: $(SRCDIR)/glob.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/glob.c >$@ $(OBJDIR)/glob.o: $(OBJDIR)/glob_.c $(OBJDIR)/glob.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/glob.o -c $(OBJDIR)/glob_.c $(OBJDIR)/glob.h: $(OBJDIR)/headers $(OBJDIR)/graph_.c: $(SRCDIR)/graph.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/graph.c >$@ $(OBJDIR)/graph.o: $(OBJDIR)/graph_.c $(OBJDIR)/graph.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/graph.o -c $(OBJDIR)/graph_.c $(OBJDIR)/graph.h: $(OBJDIR)/headers $(OBJDIR)/gzip_.c: $(SRCDIR)/gzip.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/gzip.c >$@ $(OBJDIR)/gzip.o: $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/gzip.o -c $(OBJDIR)/gzip_.c $(OBJDIR)/gzip.h: $(OBJDIR)/headers $(OBJDIR)/http_.c: $(SRCDIR)/http.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http.c >$@ $(OBJDIR)/http.o: $(OBJDIR)/http_.c $(OBJDIR)/http.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http.o -c $(OBJDIR)/http_.c $(OBJDIR)/http.h: $(OBJDIR)/headers $(OBJDIR)/http_socket_.c: $(SRCDIR)/http_socket.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http_socket.c >$@ $(OBJDIR)/http_socket.o: $(OBJDIR)/http_socket_.c $(OBJDIR)/http_socket.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_socket.o -c $(OBJDIR)/http_socket_.c $(OBJDIR)/http_socket.h: $(OBJDIR)/headers $(OBJDIR)/http_ssl_.c: $(SRCDIR)/http_ssl.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http_ssl.c >$@ $(OBJDIR)/http_ssl.o: $(OBJDIR)/http_ssl_.c $(OBJDIR)/http_ssl.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_ssl.o -c $(OBJDIR)/http_ssl_.c $(OBJDIR)/http_ssl.h: $(OBJDIR)/headers $(OBJDIR)/http_transport_.c: $(SRCDIR)/http_transport.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/http_transport.c >$@ $(OBJDIR)/http_transport.o: $(OBJDIR)/http_transport_.c $(OBJDIR)/http_transport.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_transport.o -c $(OBJDIR)/http_transport_.c $(OBJDIR)/http_transport.h: $(OBJDIR)/headers $(OBJDIR)/import_.c: $(SRCDIR)/import.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/import.c >$@ $(OBJDIR)/import.o: $(OBJDIR)/import_.c $(OBJDIR)/import.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/import.o -c $(OBJDIR)/import_.c $(OBJDIR)/import.h: $(OBJDIR)/headers $(OBJDIR)/info_.c: $(SRCDIR)/info.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/info.c >$@ $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c $(OBJDIR)/info.h: $(OBJDIR)/headers $(OBJDIR)/json_.c: $(SRCDIR)/json.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json.c >$@ $(OBJDIR)/json.o: $(OBJDIR)/json_.c $(OBJDIR)/json.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c $(OBJDIR)/json.h: $(OBJDIR)/headers $(OBJDIR)/json_artifact_.c: $(SRCDIR)/json_artifact.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_artifact.c >$@ $(OBJDIR)/json_artifact.o: $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h: $(OBJDIR)/headers $(OBJDIR)/json_branch_.c: $(SRCDIR)/json_branch.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_branch.c >$@ $(OBJDIR)/json_branch.o: $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h: $(OBJDIR)/headers $(OBJDIR)/json_config_.c: $(SRCDIR)/json_config.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_config.c >$@ $(OBJDIR)/json_config.o: $(OBJDIR)/json_config_.c $(OBJDIR)/json_config.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_config.o -c $(OBJDIR)/json_config_.c $(OBJDIR)/json_config.h: $(OBJDIR)/headers $(OBJDIR)/json_diff_.c: $(SRCDIR)/json_diff.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_diff.c >$@ $(OBJDIR)/json_diff.o: $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h: $(OBJDIR)/headers $(OBJDIR)/json_dir_.c: $(SRCDIR)/json_dir.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_dir.c >$@ $(OBJDIR)/json_dir.o: $(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_dir.o -c $(OBJDIR)/json_dir_.c $(OBJDIR)/json_dir.h: $(OBJDIR)/headers $(OBJDIR)/json_finfo_.c: $(SRCDIR)/json_finfo.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_finfo.c >$@ $(OBJDIR)/json_finfo.o: $(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_finfo.o -c $(OBJDIR)/json_finfo_.c $(OBJDIR)/json_finfo.h: $(OBJDIR)/headers $(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_login.c >$@ $(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h: $(OBJDIR)/headers $(OBJDIR)/json_query_.c: $(SRCDIR)/json_query.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_query.c >$@ $(OBJDIR)/json_query.o: $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h: $(OBJDIR)/headers $(OBJDIR)/json_report_.c: $(SRCDIR)/json_report.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_report.c >$@ $(OBJDIR)/json_report.o: $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h: $(OBJDIR)/headers $(OBJDIR)/json_status_.c: $(SRCDIR)/json_status.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_status.c >$@ $(OBJDIR)/json_status.o: $(OBJDIR)/json_status_.c $(OBJDIR)/json_status.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_status.o -c $(OBJDIR)/json_status_.c $(OBJDIR)/json_status.h: $(OBJDIR)/headers $(OBJDIR)/json_tag_.c: $(SRCDIR)/json_tag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_tag.c >$@ $(OBJDIR)/json_tag.o: $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h: $(OBJDIR)/headers $(OBJDIR)/json_timeline_.c: $(SRCDIR)/json_timeline.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_timeline.c >$@ $(OBJDIR)/json_timeline.o: $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h: $(OBJDIR)/headers $(OBJDIR)/json_user_.c: $(SRCDIR)/json_user.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_user.c >$@ $(OBJDIR)/json_user.o: $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h: $(OBJDIR)/headers $(OBJDIR)/json_wiki_.c: $(SRCDIR)/json_wiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/json_wiki.c >$@ $(OBJDIR)/json_wiki.o: $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h: $(OBJDIR)/headers $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/leaf.c >$@ $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h: $(OBJDIR)/headers $(OBJDIR)/loadctrl_.c: $(SRCDIR)/loadctrl.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/loadctrl.c >$@ $(OBJDIR)/loadctrl.o: $(OBJDIR)/loadctrl_.c $(OBJDIR)/loadctrl.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/loadctrl.o -c $(OBJDIR)/loadctrl_.c $(OBJDIR)/loadctrl.h: $(OBJDIR)/headers $(OBJDIR)/login_.c: $(SRCDIR)/login.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/login.c >$@ $(OBJDIR)/login.o: $(OBJDIR)/login_.c $(OBJDIR)/login.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/login.o -c $(OBJDIR)/login_.c $(OBJDIR)/login.h: $(OBJDIR)/headers $(OBJDIR)/lookslike_.c: $(SRCDIR)/lookslike.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/lookslike.c >$@ $(OBJDIR)/lookslike.o: $(OBJDIR)/lookslike_.c $(OBJDIR)/lookslike.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/lookslike.o -c $(OBJDIR)/lookslike_.c $(OBJDIR)/lookslike.h: $(OBJDIR)/headers $(OBJDIR)/main_.c: $(SRCDIR)/main.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/main.c >$@ $(OBJDIR)/main.o: $(OBJDIR)/main_.c $(OBJDIR)/main.h $(OBJDIR)/page_index.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/main.o -c $(OBJDIR)/main_.c $(OBJDIR)/main.h: $(OBJDIR)/headers $(OBJDIR)/manifest_.c: $(SRCDIR)/manifest.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/manifest.c >$@ $(OBJDIR)/manifest.o: $(OBJDIR)/manifest_.c $(OBJDIR)/manifest.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/manifest.o -c $(OBJDIR)/manifest_.c $(OBJDIR)/manifest.h: $(OBJDIR)/headers $(OBJDIR)/markdown_.c: $(SRCDIR)/markdown.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/markdown.c >$@ $(OBJDIR)/markdown.o: $(OBJDIR)/markdown_.c $(OBJDIR)/markdown.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown.o -c $(OBJDIR)/markdown_.c $(OBJDIR)/markdown.h: $(OBJDIR)/headers $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c $(OBJDIR)/md5.h: $(OBJDIR)/headers $(OBJDIR)/merge_.c: $(SRCDIR)/merge.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/merge.c >$@ $(OBJDIR)/merge.o: $(OBJDIR)/merge_.c $(OBJDIR)/merge.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/merge.o -c $(OBJDIR)/merge_.c $(OBJDIR)/merge.h: $(OBJDIR)/headers $(OBJDIR)/merge3_.c: $(SRCDIR)/merge3.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/merge3.c >$@ $(OBJDIR)/merge3.o: $(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h: $(OBJDIR)/headers $(OBJDIR)/moderate_.c: $(SRCDIR)/moderate.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/moderate.c >$@ $(OBJDIR)/moderate.o: $(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h: $(OBJDIR)/headers $(OBJDIR)/name_.c: $(SRCDIR)/name.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/name.c >$@ $(OBJDIR)/name.o: $(OBJDIR)/name_.c $(OBJDIR)/name.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c $(OBJDIR)/name.h: $(OBJDIR)/headers $(OBJDIR)/path_.c: $(SRCDIR)/path.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/path.c >$@ $(OBJDIR)/path.o: $(OBJDIR)/path_.c $(OBJDIR)/path.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/path.o -c $(OBJDIR)/path_.c $(OBJDIR)/path.h: $(OBJDIR)/headers $(OBJDIR)/pivot_.c: $(SRCDIR)/pivot.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/pivot.c >$@ $(OBJDIR)/pivot.o: $(OBJDIR)/pivot_.c $(OBJDIR)/pivot.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pivot.o -c $(OBJDIR)/pivot_.c $(OBJDIR)/pivot.h: $(OBJDIR)/headers $(OBJDIR)/popen_.c: $(SRCDIR)/popen.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/popen.c >$@ $(OBJDIR)/popen.o: $(OBJDIR)/popen_.c $(OBJDIR)/popen.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/popen.o -c $(OBJDIR)/popen_.c $(OBJDIR)/popen.h: $(OBJDIR)/headers $(OBJDIR)/pqueue_.c: $(SRCDIR)/pqueue.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/pqueue.c >$@ $(OBJDIR)/pqueue.o: $(OBJDIR)/pqueue_.c $(OBJDIR)/pqueue.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/pqueue.o -c $(OBJDIR)/pqueue_.c $(OBJDIR)/pqueue.h: $(OBJDIR)/headers $(OBJDIR)/printf_.c: $(SRCDIR)/printf.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/printf.c >$@ $(OBJDIR)/printf.o: $(OBJDIR)/printf_.c $(OBJDIR)/printf.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/printf.o -c $(OBJDIR)/printf_.c $(OBJDIR)/printf.h: $(OBJDIR)/headers $(OBJDIR)/publish_.c: $(SRCDIR)/publish.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/publish.c >$@ $(OBJDIR)/publish.o: $(OBJDIR)/publish_.c $(OBJDIR)/publish.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/publish.o -c $(OBJDIR)/publish_.c $(OBJDIR)/publish.h: $(OBJDIR)/headers $(OBJDIR)/purge_.c: $(SRCDIR)/purge.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/purge.c >$@ $(OBJDIR)/purge.o: $(OBJDIR)/purge_.c $(OBJDIR)/purge.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/purge.o -c $(OBJDIR)/purge_.c $(OBJDIR)/purge.h: $(OBJDIR)/headers $(OBJDIR)/rebuild_.c: $(SRCDIR)/rebuild.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/rebuild.c >$@ $(OBJDIR)/rebuild.o: $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rebuild.o -c $(OBJDIR)/rebuild_.c $(OBJDIR)/rebuild.h: $(OBJDIR)/headers $(OBJDIR)/regexp_.c: $(SRCDIR)/regexp.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/regexp.c >$@ $(OBJDIR)/regexp.o: $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/regexp.o -c $(OBJDIR)/regexp_.c $(OBJDIR)/regexp.h: $(OBJDIR)/headers $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/report.c >$@ $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c $(OBJDIR)/report.h: $(OBJDIR)/headers $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/rss.c >$@ $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c $(OBJDIR)/rss.h: $(OBJDIR)/headers $(OBJDIR)/schema_.c: $(SRCDIR)/schema.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/schema.c >$@ $(OBJDIR)/schema.o: $(OBJDIR)/schema_.c $(OBJDIR)/schema.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/schema.o -c $(OBJDIR)/schema_.c $(OBJDIR)/schema.h: $(OBJDIR)/headers $(OBJDIR)/search_.c: $(SRCDIR)/search.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/search.c >$@ $(OBJDIR)/search.o: $(OBJDIR)/search_.c $(OBJDIR)/search.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/search.o -c $(OBJDIR)/search_.c $(OBJDIR)/search.h: $(OBJDIR)/headers $(OBJDIR)/setup_.c: $(SRCDIR)/setup.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/setup.c >$@ $(OBJDIR)/setup.o: $(OBJDIR)/setup_.c $(OBJDIR)/setup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/setup.o -c $(OBJDIR)/setup_.c $(OBJDIR)/setup.h: $(OBJDIR)/headers $(OBJDIR)/sha1_.c: $(SRCDIR)/sha1.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sha1.c >$@ $(OBJDIR)/sha1.o: $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sha1.o -c $(OBJDIR)/sha1_.c $(OBJDIR)/sha1.h: $(OBJDIR)/headers $(OBJDIR)/shun_.c: $(SRCDIR)/shun.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/shun.c >$@ $(OBJDIR)/shun.o: $(OBJDIR)/shun_.c $(OBJDIR)/shun.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/shun.o -c $(OBJDIR)/shun_.c $(OBJDIR)/shun.h: $(OBJDIR)/headers $(OBJDIR)/sitemap_.c: $(SRCDIR)/sitemap.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sitemap.c >$@ $(OBJDIR)/sitemap.o: $(OBJDIR)/sitemap_.c $(OBJDIR)/sitemap.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sitemap.o -c $(OBJDIR)/sitemap_.c $(OBJDIR)/sitemap.h: $(OBJDIR)/headers $(OBJDIR)/skins_.c: $(SRCDIR)/skins.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/skins.c >$@ $(OBJDIR)/skins.o: $(OBJDIR)/skins_.c $(OBJDIR)/skins.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/skins.o -c $(OBJDIR)/skins_.c $(OBJDIR)/skins.h: $(OBJDIR)/headers $(OBJDIR)/sqlcmd_.c: $(SRCDIR)/sqlcmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sqlcmd.c >$@ $(OBJDIR)/sqlcmd.o: $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sqlcmd.o -c $(OBJDIR)/sqlcmd_.c $(OBJDIR)/sqlcmd.h: $(OBJDIR)/headers $(OBJDIR)/stash_.c: $(SRCDIR)/stash.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/stash.c >$@ $(OBJDIR)/stash.o: $(OBJDIR)/stash_.c $(OBJDIR)/stash.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/stash.o -c $(OBJDIR)/stash_.c $(OBJDIR)/stash.h: $(OBJDIR)/headers $(OBJDIR)/stat_.c: $(SRCDIR)/stat.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/stat.c >$@ $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c $(OBJDIR)/stat.h: $(OBJDIR)/headers $(OBJDIR)/statrep_.c: $(SRCDIR)/statrep.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/statrep.c >$@ $(OBJDIR)/statrep.o: $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/statrep.o -c $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h: $(OBJDIR)/headers $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/style.c >$@ $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/style.o -c $(OBJDIR)/style_.c $(OBJDIR)/style.h: $(OBJDIR)/headers $(OBJDIR)/sync_.c: $(SRCDIR)/sync.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/sync.c >$@ $(OBJDIR)/sync.o: $(OBJDIR)/sync_.c $(OBJDIR)/sync.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/sync.o -c $(OBJDIR)/sync_.c $(OBJDIR)/sync.h: $(OBJDIR)/headers $(OBJDIR)/tag_.c: $(SRCDIR)/tag.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/tag.c >$@ $(OBJDIR)/tag.o: $(OBJDIR)/tag_.c $(OBJDIR)/tag.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tag.o -c $(OBJDIR)/tag_.c $(OBJDIR)/tag.h: $(OBJDIR)/headers $(OBJDIR)/tar_.c: $(SRCDIR)/tar.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/tar.c >$@ $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c $(OBJDIR)/tar.h: $(OBJDIR)/headers $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/th_main.c >$@ $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/th_main.o -c $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h: $(OBJDIR)/headers $(OBJDIR)/timeline_.c: $(SRCDIR)/timeline.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/timeline.c >$@ $(OBJDIR)/timeline.o: $(OBJDIR)/timeline_.c $(OBJDIR)/timeline.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/timeline.o -c $(OBJDIR)/timeline_.c $(OBJDIR)/timeline.h: $(OBJDIR)/headers $(OBJDIR)/tkt_.c: $(SRCDIR)/tkt.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/tkt.c >$@ $(OBJDIR)/tkt.o: $(OBJDIR)/tkt_.c $(OBJDIR)/tkt.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tkt.o -c $(OBJDIR)/tkt_.c $(OBJDIR)/tkt.h: $(OBJDIR)/headers $(OBJDIR)/tktsetup_.c: $(SRCDIR)/tktsetup.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/tktsetup.c >$@ $(OBJDIR)/tktsetup.o: $(OBJDIR)/tktsetup_.c $(OBJDIR)/tktsetup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/tktsetup.o -c $(OBJDIR)/tktsetup_.c $(OBJDIR)/tktsetup.h: $(OBJDIR)/headers $(OBJDIR)/undo_.c: $(SRCDIR)/undo.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/undo.c >$@ $(OBJDIR)/undo.o: $(OBJDIR)/undo_.c $(OBJDIR)/undo.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/undo.o -c $(OBJDIR)/undo_.c $(OBJDIR)/undo.h: $(OBJDIR)/headers $(OBJDIR)/unicode_.c: $(SRCDIR)/unicode.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/unicode.c >$@ $(OBJDIR)/unicode.o: $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/unicode.o -c $(OBJDIR)/unicode_.c $(OBJDIR)/unicode.h: $(OBJDIR)/headers $(OBJDIR)/update_.c: $(SRCDIR)/update.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/update.c >$@ $(OBJDIR)/update.o: $(OBJDIR)/update_.c $(OBJDIR)/update.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/update.o -c $(OBJDIR)/update_.c $(OBJDIR)/update.h: $(OBJDIR)/headers $(OBJDIR)/url_.c: $(SRCDIR)/url.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/url.c >$@ $(OBJDIR)/url.o: $(OBJDIR)/url_.c $(OBJDIR)/url.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/url.o -c $(OBJDIR)/url_.c $(OBJDIR)/url.h: $(OBJDIR)/headers $(OBJDIR)/user_.c: $(SRCDIR)/user.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/user.c >$@ $(OBJDIR)/user.o: $(OBJDIR)/user_.c $(OBJDIR)/user.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/user.o -c $(OBJDIR)/user_.c $(OBJDIR)/user.h: $(OBJDIR)/headers $(OBJDIR)/utf8_.c: $(SRCDIR)/utf8.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/utf8.c >$@ $(OBJDIR)/utf8.o: $(OBJDIR)/utf8_.c $(OBJDIR)/utf8.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/utf8.o -c $(OBJDIR)/utf8_.c $(OBJDIR)/utf8.h: $(OBJDIR)/headers $(OBJDIR)/util_.c: $(SRCDIR)/util.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/util.c >$@ $(OBJDIR)/util.o: $(OBJDIR)/util_.c $(OBJDIR)/util.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/util.o -c $(OBJDIR)/util_.c $(OBJDIR)/util.h: $(OBJDIR)/headers $(OBJDIR)/verify_.c: $(SRCDIR)/verify.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/verify.c >$@ $(OBJDIR)/verify.o: $(OBJDIR)/verify_.c $(OBJDIR)/verify.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/verify.o -c $(OBJDIR)/verify_.c $(OBJDIR)/verify.h: $(OBJDIR)/headers $(OBJDIR)/vfile_.c: $(SRCDIR)/vfile.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/vfile.c >$@ $(OBJDIR)/vfile.o: $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/vfile.o -c $(OBJDIR)/vfile_.c $(OBJDIR)/vfile.h: $(OBJDIR)/headers $(OBJDIR)/wiki_.c: $(SRCDIR)/wiki.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wiki.c >$@ $(OBJDIR)/wiki.o: $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wiki.o -c $(OBJDIR)/wiki_.c $(OBJDIR)/wiki.h: $(OBJDIR)/headers $(OBJDIR)/wikiformat_.c: $(SRCDIR)/wikiformat.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wikiformat.c >$@ $(OBJDIR)/wikiformat.o: $(OBJDIR)/wikiformat_.c $(OBJDIR)/wikiformat.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wikiformat.o -c $(OBJDIR)/wikiformat_.c $(OBJDIR)/wikiformat.h: $(OBJDIR)/headers $(OBJDIR)/winfile_.c: $(SRCDIR)/winfile.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/winfile.c >$@ $(OBJDIR)/winfile.o: $(OBJDIR)/winfile_.c $(OBJDIR)/winfile.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/winfile.o -c $(OBJDIR)/winfile_.c $(OBJDIR)/winfile.h: $(OBJDIR)/headers $(OBJDIR)/winhttp_.c: $(SRCDIR)/winhttp.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/winhttp.c >$@ $(OBJDIR)/winhttp.o: $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/winhttp.o -c $(OBJDIR)/winhttp_.c $(OBJDIR)/winhttp.h: $(OBJDIR)/headers $(OBJDIR)/wysiwyg_.c: $(SRCDIR)/wysiwyg.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/wysiwyg.c >$@ $(OBJDIR)/wysiwyg.o: $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/wysiwyg.o -c $(OBJDIR)/wysiwyg_.c $(OBJDIR)/wysiwyg.h: $(OBJDIR)/headers $(OBJDIR)/xfer_.c: $(SRCDIR)/xfer.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/xfer.c >$@ $(OBJDIR)/xfer.o: $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/xfer.o -c $(OBJDIR)/xfer_.c $(OBJDIR)/xfer.h: $(OBJDIR)/headers $(OBJDIR)/xfersetup_.c: $(SRCDIR)/xfersetup.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/xfersetup.c >$@ $(OBJDIR)/xfersetup.o: $(OBJDIR)/xfersetup_.c $(OBJDIR)/xfersetup.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/xfersetup.o -c $(OBJDIR)/xfersetup_.c $(OBJDIR)/xfersetup.h: $(OBJDIR)/headers $(OBJDIR)/zip_.c: $(SRCDIR)/zip.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/zip.c >$@ $(OBJDIR)/zip.o: $(OBJDIR)/zip_.c $(OBJDIR)/zip.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/zip.o -c $(OBJDIR)/zip_.c $(OBJDIR)/zip.h: $(OBJDIR)/headers SQLITE_OPTIONS = -DNDEBUG=1 \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DSQLITE_ENABLE_LOCKING_STYLE=0 \ -DSQLITE_THREADSAFE=0 \ -DSQLITE_DEFAULT_FILE_FORMAT=4 \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_ENABLE_FTS3_PARENTHESIS \ -DSQLITE_WIN32_NO_ANSI \ -D_HAVE__MINGW_H \ -DSQLITE_USE_MALLOC_H \ -DSQLITE_USE_MSIZE SHELL_OPTIONS = -Dmain=sqlite3_shell \ -DSQLITE_OMIT_LOAD_EXTENSION=1 \ -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ -DSQLITE_SHELL_DBNAME_PROC=fossil_open \ -Daccess=file_access \ -Dsystem=fossil_system \ -Dgetenv=fossil_getenv \ -Dfopen=fossil_fopen MINIZ_OPTIONS = -DMINIZ_NO_STDIO \ -DMINIZ_NO_TIME \ -DMINIZ_NO_ARCHIVE_APIS $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c $(SRCDIR)/../win/Makefile.mingw.mistachkin $(XTCC) $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) -c $(SRCDIR)/sqlite3.c -o $@ $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $@ $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/jsos_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_status.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h $(SRCDIR)/../win/Makefile.mingw.mistachkin $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)/shell.c -o $@ $(OBJDIR)/th.o: $(SRCDIR)/th.c $(XTCC) -c $(SRCDIR)/th.c -o $@ $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c $(XTCC) -c $(SRCDIR)/th_lang.c -o $@ $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c $(XTCC) -c $(SRCDIR)/th_tcl.c -o $@ $(OBJDIR)/miniz.o: $(SRCDIR)/miniz.c $(XTCC) $(MINIZ_OPTIONS) -c $(SRCDIR)/miniz.c -o $@ |
Changes to win/Makefile.msc.
1 2 3 4 5 6 7 8 9 | # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl") ############################################################################## # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # | > > > > > > > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | > > > > > | > > | > > > > > > > > > > > > > > > > > > > > | | | | | > > > > > > > > > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > | > > | < | > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | # ############################################################################## # WARNING: DO NOT EDIT, AUTOMATICALLY GENERATED FILE (SEE "src/makemake.tcl") ############################################################################## # # This Makefile will only function correctly if used from a sub-directory # that is a direct child of the top-level directory for this project. # !if !exist("..\.fossil-settings") !error "Please change the current directory to the one containing this file." !endif # # This file is automatically generated. Instead of editing this # file, edit "makemake.tcl" then run "tclsh makemake.tcl" # to regenerate this file. # B = .. SRCDIR = $B\src OBJDIR = . OX = . O = .obj E = .exe P = .pdb # Perl is only necessary if OpenSSL support is enabled and it must # be built from source code. The PERLDIR variable should point to # the directory containing the main Perl binary (i.e. "perl.exe"). PERLDIR = C:\Perl\bin PERL = perl.exe # Uncomment to enable debug symbols # DEBUG = 1 # Uncomment to support Windows XP with Visual Studio 201x # FOSSIL_ENABLE_WINXP = 1 # Uncomment to enable JSON API # FOSSIL_ENABLE_JSON = 1 # Uncomment to enable miniz usage # FOSSIL_ENABLE_MINIZ = 1 # Uncomment to enable SSL support # FOSSIL_ENABLE_SSL = 1 # Uncomment to build SSL libraries # FOSSIL_BUILD_SSL = 1 # Uncomment to enable TH1 scripts in embedded documentation files # FOSSIL_ENABLE_TH1_DOCS = 1 # Uncomment to enable TH1 hooks # FOSSIL_ENABLE_TH1_HOOKS = 1 # Uncomment to enable Tcl support # FOSSIL_ENABLE_TCL = 1 !ifdef FOSSIL_ENABLE_SSL SSLDIR = $(B)\compat\openssl-1.0.2a SSLINCDIR = $(SSLDIR)\inc32 SSLLIBDIR = $(SSLDIR)\out32 SSLLFLAGS = /nologo /opt:ref /debug SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" !message Using 'x64' platform for OpenSSL... # BUGBUG (OpenSSL): Apparently, using "no-ssl*" here breaks the build. # SSLCONFIG = VC-WIN64A no-asm no-ssl2 no-ssl3 no-shared SSLCONFIG = VC-WIN64A no-asm no-shared SSLSETUP = ms\do_win64a.bat SSLNMAKE = ms\nt.mak all SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 !elseif "$(PLATFORM)"=="ia64" !message Using 'ia64' platform for OpenSSL... # BUGBUG (OpenSSL): Apparently, using "no-ssl*" here breaks the build. # SSLCONFIG = VC-WIN64I no-asm no-ssl2 no-ssl3 no-shared SSLCONFIG = VC-WIN64I no-asm no-shared SSLSETUP = ms\do_win64i.bat SSLNMAKE = ms\nt.mak all SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 !else !message Assuming 'x86' platform for OpenSSL... # BUGBUG (OpenSSL): Apparently, using "no-ssl*" here breaks the build. # SSLCONFIG = VC-WIN32 no-asm no-ssl2 no-ssl3 no-shared SSLCONFIG = VC-WIN32 no-asm no-shared SSLSETUP = ms\do_ms.bat SSLNMAKE = ms\nt.mak all SSLCFLAGS = -DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3 !endif !endif !ifdef FOSSIL_ENABLE_TCL TCLDIR = $(B)\compat\tcl-8.6 TCLSRCDIR = $(TCLDIR) TCLINCDIR = $(TCLSRCDIR)\generic !endif # zlib options ZINCDIR = $(B)\compat\zlib ZLIBDIR = $(B)\compat\zlib ZLIB = zlib.lib INCL = /I. /I$(SRCDIR) /I$B\win\include !ifndef FOSSIL_ENABLE_MINIZ INCL = $(INCL) /I$(ZINCDIR) !endif !ifdef FOSSIL_ENABLE_SSL INCL = $(INCL) /I$(SSLINCDIR) !endif !ifdef FOSSIL_ENABLE_TCL INCL = $(INCL) /I$(TCLINCDIR) !endif CFLAGS = /nologo LDFLAGS = /NODEFAULTLIB:msvcrt /MANIFEST:NO !ifdef FOSSIL_ENABLE_WINXP XPCFLAGS = $(XPCFLAGS) /D_USING_V110_SDK71_=1 CFLAGS = $(CFLAGS) $(XPCFLAGS) !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" XPLDFLAGS = $(XPLDFLAGS) /SUBSYSTEM:CONSOLE,5.02 !else XPLDFLAGS = $(XPLDFLAGS) /SUBSYSTEM:CONSOLE,5.01 !endif LDFLAGS = $(LDFLAGS) $(XPLDFLAGS) !endif !ifdef DEBUG CFLAGS = $(CFLAGS) /Zi /MTd /Od LDFLAGS = $(LDFLAGS) /DEBUG !else CFLAGS = $(CFLAGS) /MT /O2 !endif BCC = $(CC) $(CFLAGS) TCC = $(CC) /c $(CFLAGS) $(MSCDEF) $(INCL) RCC = rc /D_WIN32 /D_MSC_VER $(MSCDEF) $(INCL) LIBS = ws2_32.lib advapi32.lib LIBDIR = !ifndef FOSSIL_ENABLE_MINIZ LIBS = $(LIBS) $(ZLIB) LIBDIR = $(LIBDIR) /LIBPATH:$(ZLIBDIR) !endif !ifdef FOSSIL_ENABLE_MINIZ TCC = $(TCC) /DFOSSIL_ENABLE_MINIZ=1 RCC = $(RCC) /DFOSSIL_ENABLE_MINIZ=1 !endif !ifdef FOSSIL_ENABLE_JSON TCC = $(TCC) /DFOSSIL_ENABLE_JSON=1 RCC = $(RCC) /DFOSSIL_ENABLE_JSON=1 !endif !ifdef FOSSIL_ENABLE_SSL TCC = $(TCC) /DFOSSIL_ENABLE_SSL=1 RCC = $(RCC) /DFOSSIL_ENABLE_SSL=1 LIBS = $(LIBS) $(SSLLIB) LIBDIR = $(LIBDIR) /LIBPATH:$(SSLLIBDIR) !endif !ifdef FOSSIL_ENABLE_TH1_DOCS TCC = $(TCC) /DFOSSIL_ENABLE_TH1_DOCS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TH1_DOCS=1 !endif !ifdef FOSSIL_ENABLE_TH1_HOOKS TCC = $(TCC) /DFOSSIL_ENABLE_TH1_HOOKS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TH1_HOOKS=1 !endif !ifdef FOSSIL_ENABLE_TCL TCC = $(TCC) /DFOSSIL_ENABLE_TCL=1 RCC = $(RCC) /DFOSSIL_ENABLE_TCL=1 TCC = $(TCC) /DFOSSIL_ENABLE_TCL_STUBS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TCL_STUBS=1 TCC = $(TCC) /DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 RCC = $(RCC) /DFOSSIL_ENABLE_TCL_PRIVATE_STUBS=1 TCC = $(TCC) /DUSE_TCL_STUBS=1 RCC = $(RCC) /DUSE_TCL_STUBS=1 !endif SQLITE_OPTIONS = /DNDEBUG=1 \ /DSQLITE_OMIT_LOAD_EXTENSION=1 \ /DSQLITE_ENABLE_LOCKING_STYLE=0 \ /DSQLITE_THREADSAFE=0 \ /DSQLITE_DEFAULT_FILE_FORMAT=4 \ /DSQLITE_OMIT_DEPRECATED \ /DSQLITE_ENABLE_EXPLAIN_COMMENTS \ /DSQLITE_ENABLE_FTS4 \ /DSQLITE_ENABLE_FTS3_PARENTHESIS \ /DSQLITE_WIN32_NO_ANSI SHELL_OPTIONS = /Dmain=sqlite3_shell \ /DSQLITE_OMIT_LOAD_EXTENSION=1 \ /DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) \ /DSQLITE_SHELL_DBNAME_PROC=fossil_open \ /Daccess=file_access \ /Dsystem=fossil_system \ /Dgetenv=fossil_getenv \ /Dfopen=fossil_fopen MINIZ_OPTIONS = /DMINIZ_NO_STDIO \ /DMINIZ_NO_TIME \ /DMINIZ_NO_ARCHIVE_APIS SRC = add_.c \ allrepo_.c \ attach_.c \ bag_.c \ bisect_.c \ blob_.c \ branch_.c \ browse_.c \ builtin_.c \ bundle_.c \ cache_.c \ captcha_.c \ cgi_.c \ checkin_.c \ checkout_.c \ clearsign_.c \ clone_.c \ comformat_.c \ |
︙ | ︙ | |||
89 90 91 92 93 94 95 96 97 98 99 100 101 102 | diffcmd_.c \ doc_.c \ encode_.c \ event_.c \ export_.c \ file_.c \ finfo_.c \ glob_.c \ graph_.c \ gzip_.c \ http_.c \ http_socket_.c \ http_ssl_.c \ http_transport_.c \ | > > | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | diffcmd_.c \ doc_.c \ encode_.c \ event_.c \ export_.c \ file_.c \ finfo_.c \ foci_.c \ fusefs_.c \ glob_.c \ graph_.c \ gzip_.c \ http_.c \ http_socket_.c \ http_ssl_.c \ http_transport_.c \ |
︙ | ︙ | |||
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | json_report_.c \ json_status_.c \ json_tag_.c \ json_timeline_.c \ json_user_.c \ json_wiki_.c \ leaf_.c \ login_.c \ lookslike_.c \ main_.c \ manifest_.c \ markdown_.c \ markdown_html_.c \ md5_.c \ merge_.c \ merge3_.c \ moderate_.c \ name_.c \ path_.c \ pivot_.c \ popen_.c \ pqueue_.c \ printf_.c \ rebuild_.c \ regexp_.c \ report_.c \ rss_.c \ schema_.c \ search_.c \ setup_.c \ sha1_.c \ shun_.c \ skins_.c \ sqlcmd_.c \ stash_.c \ stat_.c \ style_.c \ sync_.c \ tag_.c \ tar_.c \ th_main_.c \ timeline_.c \ tkt_.c \ tktsetup_.c \ undo_.c \ unicode_.c \ update_.c \ url_.c \ user_.c \ utf8_.c \ util_.c \ verify_.c \ vfile_.c \ wiki_.c \ wikiformat_.c \ winhttp_.c \ wysiwyg_.c \ xfer_.c \ xfersetup_.c \ zip_.c OBJ = $(OX)\add$O \ $(OX)\allrepo$O \ $(OX)\attach$O \ $(OX)\bag$O \ $(OX)\bisect$O \ $(OX)\blob$O \ $(OX)\branch$O \ $(OX)\browse$O \ $(OX)\captcha$O \ $(OX)\cgi$O \ $(OX)\checkin$O \ $(OX)\checkout$O \ $(OX)\clearsign$O \ $(OX)\clone$O \ $(OX)\comformat$O \ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | json_report_.c \ json_status_.c \ json_tag_.c \ json_timeline_.c \ json_user_.c \ json_wiki_.c \ leaf_.c \ loadctrl_.c \ login_.c \ lookslike_.c \ main_.c \ manifest_.c \ markdown_.c \ markdown_html_.c \ md5_.c \ merge_.c \ merge3_.c \ moderate_.c \ name_.c \ path_.c \ pivot_.c \ popen_.c \ pqueue_.c \ printf_.c \ publish_.c \ purge_.c \ rebuild_.c \ regexp_.c \ report_.c \ rss_.c \ schema_.c \ search_.c \ setup_.c \ sha1_.c \ shun_.c \ sitemap_.c \ skins_.c \ sqlcmd_.c \ stash_.c \ stat_.c \ statrep_.c \ style_.c \ sync_.c \ tag_.c \ tar_.c \ th_main_.c \ timeline_.c \ tkt_.c \ tktsetup_.c \ undo_.c \ unicode_.c \ update_.c \ url_.c \ user_.c \ utf8_.c \ util_.c \ verify_.c \ vfile_.c \ wiki_.c \ wikiformat_.c \ winfile_.c \ winhttp_.c \ wysiwyg_.c \ xfer_.c \ xfersetup_.c \ zip_.c EXTRA_FILES = $(SRCDIR)\../skins/aht/details.txt \ $(SRCDIR)\../skins/black_and_white/css.txt \ $(SRCDIR)\../skins/black_and_white/details.txt \ $(SRCDIR)\../skins/black_and_white/footer.txt \ $(SRCDIR)\../skins/black_and_white/header.txt \ $(SRCDIR)\../skins/blitz/css.txt \ $(SRCDIR)\../skins/blitz/details.txt \ $(SRCDIR)\../skins/blitz/footer.txt \ $(SRCDIR)\../skins/blitz/header.txt \ $(SRCDIR)\../skins/blitz/ticket.txt \ $(SRCDIR)\../skins/blitz_no_logo/css.txt \ $(SRCDIR)\../skins/blitz_no_logo/details.txt \ $(SRCDIR)\../skins/blitz_no_logo/footer.txt \ $(SRCDIR)\../skins/blitz_no_logo/header.txt \ $(SRCDIR)\../skins/blitz_no_logo/ticket.txt \ $(SRCDIR)\../skins/default/css.txt \ $(SRCDIR)\../skins/default/details.txt \ $(SRCDIR)\../skins/default/footer.txt \ $(SRCDIR)\../skins/default/header.txt \ $(SRCDIR)\../skins/eagle/css.txt \ $(SRCDIR)\../skins/eagle/details.txt \ $(SRCDIR)\../skins/eagle/footer.txt \ $(SRCDIR)\../skins/eagle/header.txt \ $(SRCDIR)\../skins/enhanced1/css.txt \ $(SRCDIR)\../skins/enhanced1/details.txt \ $(SRCDIR)\../skins/enhanced1/footer.txt \ $(SRCDIR)\../skins/enhanced1/header.txt \ $(SRCDIR)\../skins/khaki/css.txt \ $(SRCDIR)\../skins/khaki/details.txt \ $(SRCDIR)\../skins/khaki/footer.txt \ $(SRCDIR)\../skins/khaki/header.txt \ $(SRCDIR)\../skins/original/css.txt \ $(SRCDIR)\../skins/original/details.txt \ $(SRCDIR)\../skins/original/footer.txt \ $(SRCDIR)\../skins/original/header.txt \ $(SRCDIR)\../skins/plain_gray/css.txt \ $(SRCDIR)\../skins/plain_gray/details.txt \ $(SRCDIR)\../skins/plain_gray/footer.txt \ $(SRCDIR)\../skins/plain_gray/header.txt \ $(SRCDIR)\../skins/rounded1/css.txt \ $(SRCDIR)\../skins/rounded1/details.txt \ $(SRCDIR)\../skins/rounded1/footer.txt \ $(SRCDIR)\../skins/rounded1/header.txt \ $(SRCDIR)\../skins/xekri/css.txt \ $(SRCDIR)\../skins/xekri/details.txt \ $(SRCDIR)\../skins/xekri/footer.txt \ $(SRCDIR)\../skins/xekri/header.txt \ $(SRCDIR)\diff.tcl \ $(SRCDIR)\markdown.md OBJ = $(OX)\add$O \ $(OX)\allrepo$O \ $(OX)\attach$O \ $(OX)\bag$O \ $(OX)\bisect$O \ $(OX)\blob$O \ $(OX)\branch$O \ $(OX)\browse$O \ $(OX)\builtin$O \ $(OX)\bundle$O \ $(OX)\cache$O \ $(OX)\captcha$O \ $(OX)\cgi$O \ $(OX)\checkin$O \ $(OX)\checkout$O \ $(OX)\clearsign$O \ $(OX)\clone$O \ $(OX)\comformat$O \ |
︙ | ︙ | |||
198 199 200 201 202 203 204 205 206 207 208 209 210 211 | $(OX)\diffcmd$O \ $(OX)\doc$O \ $(OX)\encode$O \ $(OX)\event$O \ $(OX)\export$O \ $(OX)\file$O \ $(OX)\finfo$O \ $(OX)\glob$O \ $(OX)\graph$O \ $(OX)\gzip$O \ $(OX)\http$O \ $(OX)\http_socket$O \ $(OX)\http_ssl$O \ $(OX)\http_transport$O \ | > > | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 | $(OX)\diffcmd$O \ $(OX)\doc$O \ $(OX)\encode$O \ $(OX)\event$O \ $(OX)\export$O \ $(OX)\file$O \ $(OX)\finfo$O \ $(OX)\foci$O \ $(OX)\fusefs$O \ $(OX)\glob$O \ $(OX)\graph$O \ $(OX)\gzip$O \ $(OX)\http$O \ $(OX)\http_socket$O \ $(OX)\http_ssl$O \ $(OX)\http_transport$O \ |
︙ | ︙ | |||
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | $(OX)\json_report$O \ $(OX)\json_status$O \ $(OX)\json_tag$O \ $(OX)\json_timeline$O \ $(OX)\json_user$O \ $(OX)\json_wiki$O \ $(OX)\leaf$O \ $(OX)\login$O \ $(OX)\lookslike$O \ $(OX)\main$O \ $(OX)\manifest$O \ $(OX)\markdown$O \ $(OX)\markdown_html$O \ $(OX)\md5$O \ $(OX)\merge$O \ $(OX)\merge3$O \ $(OX)\moderate$O \ $(OX)\name$O \ $(OX)\path$O \ $(OX)\pivot$O \ $(OX)\popen$O \ $(OX)\pqueue$O \ $(OX)\printf$O \ $(OX)\rebuild$O \ $(OX)\regexp$O \ $(OX)\report$O \ $(OX)\rss$O \ $(OX)\schema$O \ $(OX)\search$O \ $(OX)\setup$O \ $(OX)\sha1$O \ $(OX)\shell$O \ $(OX)\shun$O \ $(OX)\skins$O \ $(OX)\sqlcmd$O \ $(OX)\sqlite3$O \ $(OX)\stash$O \ $(OX)\stat$O \ $(OX)\style$O \ $(OX)\sync$O \ $(OX)\tag$O \ $(OX)\tar$O \ $(OX)\th$O \ $(OX)\th_lang$O \ $(OX)\th_main$O \ $(OX)\timeline$O \ $(OX)\tkt$O \ $(OX)\tktsetup$O \ $(OX)\undo$O \ $(OX)\unicode$O \ $(OX)\update$O \ $(OX)\url$O \ $(OX)\user$O \ $(OX)\utf8$O \ $(OX)\util$O \ $(OX)\verify$O \ $(OX)\vfile$O \ $(OX)\wiki$O \ $(OX)\wikiformat$O \ $(OX)\winhttp$O \ $(OX)\wysiwyg$O \ $(OX)\xfer$O \ $(OX)\xfersetup$O \ $(OX)\zip$O \ $(OX)\fossil.res | > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > | > | > > > | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | $(OX)\json_report$O \ $(OX)\json_status$O \ $(OX)\json_tag$O \ $(OX)\json_timeline$O \ $(OX)\json_user$O \ $(OX)\json_wiki$O \ $(OX)\leaf$O \ $(OX)\loadctrl$O \ $(OX)\login$O \ $(OX)\lookslike$O \ $(OX)\main$O \ $(OX)\manifest$O \ $(OX)\markdown$O \ $(OX)\markdown_html$O \ $(OX)\md5$O \ $(OX)\merge$O \ $(OX)\merge3$O \ $(OX)\moderate$O \ $(OX)\name$O \ $(OX)\path$O \ $(OX)\pivot$O \ $(OX)\popen$O \ $(OX)\pqueue$O \ $(OX)\printf$O \ $(OX)\publish$O \ $(OX)\purge$O \ $(OX)\rebuild$O \ $(OX)\regexp$O \ $(OX)\report$O \ $(OX)\rss$O \ $(OX)\schema$O \ $(OX)\search$O \ $(OX)\setup$O \ $(OX)\sha1$O \ $(OX)\shell$O \ $(OX)\shun$O \ $(OX)\sitemap$O \ $(OX)\skins$O \ $(OX)\sqlcmd$O \ $(OX)\sqlite3$O \ $(OX)\stash$O \ $(OX)\stat$O \ $(OX)\statrep$O \ $(OX)\style$O \ $(OX)\sync$O \ $(OX)\tag$O \ $(OX)\tar$O \ $(OX)\th$O \ $(OX)\th_lang$O \ $(OX)\th_main$O \ $(OX)\th_tcl$O \ $(OX)\timeline$O \ $(OX)\tkt$O \ $(OX)\tktsetup$O \ $(OX)\undo$O \ $(OX)\unicode$O \ $(OX)\update$O \ $(OX)\url$O \ $(OX)\user$O \ $(OX)\utf8$O \ $(OX)\util$O \ $(OX)\verify$O \ $(OX)\vfile$O \ $(OX)\wiki$O \ $(OX)\wikiformat$O \ $(OX)\winfile$O \ $(OX)\winhttp$O \ $(OX)\wysiwyg$O \ $(OX)\xfer$O \ $(OX)\xfersetup$O \ $(OX)\zip$O \ !ifdef FOSSIL_ENABLE_MINIZ $(OX)\miniz$O \ !endif $(OX)\fossil.res APPNAME = $(OX)\fossil$(E) PDBNAME = $(OX)\fossil$(P) APPTARGETS = all: $(OX) $(APPNAME) zlib: @echo Building zlib from "$(ZLIBDIR)"... !ifdef FOSSIL_ENABLE_WINXP @pushd "$(ZLIBDIR)" && $(MAKE) /f win32\Makefile.msc $(ZLIB) "CC=cl $(XPCFLAGS)" "LD=link $(XPLDFLAGS)" && popd !else @pushd "$(ZLIBDIR)" && $(MAKE) /f win32\Makefile.msc $(ZLIB) && popd !endif !ifdef FOSSIL_ENABLE_SSL openssl: @echo Building OpenSSL from "$(SSLDIR)"... !if "$(PERLDIR)" != "" @set PATH=$(PERLDIR);$(PATH) !endif @pushd "$(SSLDIR)" && $(PERL) Configure $(SSLCONFIG) && popd @pushd "$(SSLDIR)" && call $(SSLSETUP) && popd !ifdef FOSSIL_ENABLE_WINXP @pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) "CC=cl $(SSLCFLAGS) $(XPCFLAGS)" "LFLAGS=$(SSLLFLAGS) $(XPLDFLAGS)" && popd !else @pushd "$(SSLDIR)" && $(MAKE) /f $(SSLNMAKE) "CC=cl $(SSLCFLAGS)" && popd !endif !endif !ifndef FOSSIL_ENABLE_MINIZ APPTARGETS = $(APPTARGETS) zlib !endif !ifdef FOSSIL_ENABLE_SSL !ifdef FOSSIL_BUILD_SSL APPTARGETS = $(APPTARGETS) openssl !endif !endif $(APPNAME) : $(APPTARGETS) translate$E mkindex$E codecheck1$E headers $(OBJ) $(OX)\linkopts cd $(OX) codecheck1$E $(SRC) link $(LDFLAGS) /OUT:$@ $(LIBDIR) Wsetargv.obj fossil.res @linkopts $(OX)\linkopts: $B\win\Makefile.msc echo $(OX)\add.obj > $@ echo $(OX)\allrepo.obj >> $@ echo $(OX)\attach.obj >> $@ echo $(OX)\bag.obj >> $@ echo $(OX)\bisect.obj >> $@ echo $(OX)\blob.obj >> $@ echo $(OX)\branch.obj >> $@ echo $(OX)\browse.obj >> $@ echo $(OX)\builtin.obj >> $@ echo $(OX)\bundle.obj >> $@ echo $(OX)\cache.obj >> $@ echo $(OX)\captcha.obj >> $@ echo $(OX)\cgi.obj >> $@ echo $(OX)\checkin.obj >> $@ echo $(OX)\checkout.obj >> $@ echo $(OX)\clearsign.obj >> $@ echo $(OX)\clone.obj >> $@ echo $(OX)\comformat.obj >> $@ |
︙ | ︙ | |||
325 326 327 328 329 330 331 332 333 334 335 336 337 338 | echo $(OX)\diffcmd.obj >> $@ echo $(OX)\doc.obj >> $@ echo $(OX)\encode.obj >> $@ echo $(OX)\event.obj >> $@ echo $(OX)\export.obj >> $@ echo $(OX)\file.obj >> $@ echo $(OX)\finfo.obj >> $@ echo $(OX)\glob.obj >> $@ echo $(OX)\graph.obj >> $@ echo $(OX)\gzip.obj >> $@ echo $(OX)\http.obj >> $@ echo $(OX)\http_socket.obj >> $@ echo $(OX)\http_ssl.obj >> $@ echo $(OX)\http_transport.obj >> $@ | > > | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 | echo $(OX)\diffcmd.obj >> $@ echo $(OX)\doc.obj >> $@ echo $(OX)\encode.obj >> $@ echo $(OX)\event.obj >> $@ echo $(OX)\export.obj >> $@ echo $(OX)\file.obj >> $@ echo $(OX)\finfo.obj >> $@ echo $(OX)\foci.obj >> $@ echo $(OX)\fusefs.obj >> $@ echo $(OX)\glob.obj >> $@ echo $(OX)\graph.obj >> $@ echo $(OX)\gzip.obj >> $@ echo $(OX)\http.obj >> $@ echo $(OX)\http_socket.obj >> $@ echo $(OX)\http_ssl.obj >> $@ echo $(OX)\http_transport.obj >> $@ |
︙ | ︙ | |||
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | echo $(OX)\json_report.obj >> $@ echo $(OX)\json_status.obj >> $@ echo $(OX)\json_tag.obj >> $@ echo $(OX)\json_timeline.obj >> $@ echo $(OX)\json_user.obj >> $@ echo $(OX)\json_wiki.obj >> $@ echo $(OX)\leaf.obj >> $@ echo $(OX)\login.obj >> $@ echo $(OX)\lookslike.obj >> $@ echo $(OX)\main.obj >> $@ echo $(OX)\manifest.obj >> $@ echo $(OX)\markdown.obj >> $@ echo $(OX)\markdown_html.obj >> $@ echo $(OX)\md5.obj >> $@ echo $(OX)\merge.obj >> $@ echo $(OX)\merge3.obj >> $@ echo $(OX)\moderate.obj >> $@ echo $(OX)\name.obj >> $@ echo $(OX)\path.obj >> $@ echo $(OX)\pivot.obj >> $@ echo $(OX)\popen.obj >> $@ echo $(OX)\pqueue.obj >> $@ echo $(OX)\printf.obj >> $@ echo $(OX)\rebuild.obj >> $@ echo $(OX)\regexp.obj >> $@ echo $(OX)\report.obj >> $@ echo $(OX)\rss.obj >> $@ echo $(OX)\schema.obj >> $@ echo $(OX)\search.obj >> $@ echo $(OX)\setup.obj >> $@ echo $(OX)\sha1.obj >> $@ echo $(OX)\shell.obj >> $@ echo $(OX)\shun.obj >> $@ echo $(OX)\skins.obj >> $@ echo $(OX)\sqlcmd.obj >> $@ echo $(OX)\sqlite3.obj >> $@ echo $(OX)\stash.obj >> $@ echo $(OX)\stat.obj >> $@ echo $(OX)\style.obj >> $@ echo $(OX)\sync.obj >> $@ echo $(OX)\tag.obj >> $@ echo $(OX)\tar.obj >> $@ echo $(OX)\th.obj >> $@ echo $(OX)\th_lang.obj >> $@ echo $(OX)\th_main.obj >> $@ echo $(OX)\timeline.obj >> $@ echo $(OX)\tkt.obj >> $@ echo $(OX)\tktsetup.obj >> $@ echo $(OX)\undo.obj >> $@ echo $(OX)\unicode.obj >> $@ echo $(OX)\update.obj >> $@ echo $(OX)\url.obj >> $@ echo $(OX)\user.obj >> $@ echo $(OX)\utf8.obj >> $@ echo $(OX)\util.obj >> $@ echo $(OX)\verify.obj >> $@ echo $(OX)\vfile.obj >> $@ echo $(OX)\wiki.obj >> $@ echo $(OX)\wikiformat.obj >> $@ echo $(OX)\winhttp.obj >> $@ echo $(OX)\wysiwyg.obj >> $@ echo $(OX)\xfer.obj >> $@ echo $(OX)\xfersetup.obj >> $@ echo $(OX)\zip.obj >> $@ | > > > > > > > | | | | > > > | > > > | | | | > > > > > > | | > > > | | | | > | | | | | | > | > | > | > | > > > > > < | 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | echo $(OX)\json_report.obj >> $@ echo $(OX)\json_status.obj >> $@ echo $(OX)\json_tag.obj >> $@ echo $(OX)\json_timeline.obj >> $@ echo $(OX)\json_user.obj >> $@ echo $(OX)\json_wiki.obj >> $@ echo $(OX)\leaf.obj >> $@ echo $(OX)\loadctrl.obj >> $@ echo $(OX)\login.obj >> $@ echo $(OX)\lookslike.obj >> $@ echo $(OX)\main.obj >> $@ echo $(OX)\manifest.obj >> $@ echo $(OX)\markdown.obj >> $@ echo $(OX)\markdown_html.obj >> $@ echo $(OX)\md5.obj >> $@ echo $(OX)\merge.obj >> $@ echo $(OX)\merge3.obj >> $@ echo $(OX)\moderate.obj >> $@ echo $(OX)\name.obj >> $@ echo $(OX)\path.obj >> $@ echo $(OX)\pivot.obj >> $@ echo $(OX)\popen.obj >> $@ echo $(OX)\pqueue.obj >> $@ echo $(OX)\printf.obj >> $@ echo $(OX)\publish.obj >> $@ echo $(OX)\purge.obj >> $@ echo $(OX)\rebuild.obj >> $@ echo $(OX)\regexp.obj >> $@ echo $(OX)\report.obj >> $@ echo $(OX)\rss.obj >> $@ echo $(OX)\schema.obj >> $@ echo $(OX)\search.obj >> $@ echo $(OX)\setup.obj >> $@ echo $(OX)\sha1.obj >> $@ echo $(OX)\shell.obj >> $@ echo $(OX)\shun.obj >> $@ echo $(OX)\sitemap.obj >> $@ echo $(OX)\skins.obj >> $@ echo $(OX)\sqlcmd.obj >> $@ echo $(OX)\sqlite3.obj >> $@ echo $(OX)\stash.obj >> $@ echo $(OX)\stat.obj >> $@ echo $(OX)\statrep.obj >> $@ echo $(OX)\style.obj >> $@ echo $(OX)\sync.obj >> $@ echo $(OX)\tag.obj >> $@ echo $(OX)\tar.obj >> $@ echo $(OX)\th.obj >> $@ echo $(OX)\th_lang.obj >> $@ echo $(OX)\th_main.obj >> $@ echo $(OX)\th_tcl.obj >> $@ echo $(OX)\timeline.obj >> $@ echo $(OX)\tkt.obj >> $@ echo $(OX)\tktsetup.obj >> $@ echo $(OX)\undo.obj >> $@ echo $(OX)\unicode.obj >> $@ echo $(OX)\update.obj >> $@ echo $(OX)\url.obj >> $@ echo $(OX)\user.obj >> $@ echo $(OX)\utf8.obj >> $@ echo $(OX)\util.obj >> $@ echo $(OX)\verify.obj >> $@ echo $(OX)\vfile.obj >> $@ echo $(OX)\wiki.obj >> $@ echo $(OX)\wikiformat.obj >> $@ echo $(OX)\winfile.obj >> $@ echo $(OX)\winhttp.obj >> $@ echo $(OX)\wysiwyg.obj >> $@ echo $(OX)\xfer.obj >> $@ echo $(OX)\xfersetup.obj >> $@ echo $(OX)\zip.obj >> $@ !ifdef FOSSIL_ENABLE_MINIZ echo $(OX)\miniz.obj >> $@ !endif echo $(LIBS) >> $@ $(OX): @-mkdir $@ translate$E: $(SRCDIR)\translate.c $(BCC) $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) $** mkbuiltin$E: $(SRCDIR)\mkbuiltin.c $(BCC) $** mkversion$E: $(SRCDIR)\mkversion.c $(BCC) $** codecheck1$E: $(SRCDIR)\codecheck1.c $(BCC) $** $(OX)\shell$O : $(SRCDIR)\shell.c $B\win\Makefile.msc $(TCC) /Fo$@ $(SHELL_OPTIONS) $(SQLITE_OPTIONS) $(SHELL_CFLAGS) -c $(SRCDIR)\shell.c $(OX)\sqlite3$O : $(SRCDIR)\sqlite3.c $B\win\Makefile.msc $(TCC) /Fo$@ -c $(SQLITE_OPTIONS) $(SQLITE_CFLAGS) $(SRCDIR)\sqlite3.c $(OX)\th$O : $(SRCDIR)\th.c $(TCC) /Fo$@ -c $** $(OX)\th_lang$O : $(SRCDIR)\th_lang.c $(TCC) /Fo$@ -c $** $(OX)\th_tcl$O : $(SRCDIR)\th_tcl.c $(TCC) /Fo$@ -c $** $(OX)\miniz$O : $(SRCDIR)\miniz.c $(TCC) /Fo$@ -c $(MINIZ_OPTIONS) $(SRCDIR)\miniz.c VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION $** > $@ $(OX)\cson_amalgamation$O : $(SRCDIR)\cson_amalgamation.c $(TCC) /Fo$@ /c $** page_index.h: mkindex$E $(SRC) $** > $@ builtin_data.h: mkbuiltin$E $(EXTRA_FILES) mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ clean: del $(OX)\*.obj 2>NUL del *.obj 2>NUL del *_.c 2>NUL del *.h 2>NUL del *.ilk 2>NUL del *.map 2>NUL del *.res 2>NUL del headers 2>NUL del linkopts 2>NUL del vc*.pdb 2>NUL realclean: clean del $(APPNAME) 2>NUL del $(PDBNAME) 2>NUL del translate$E 2>NUL del translate$P 2>NUL del mkindex$E 2>NUL del mkindex$P 2>NUL del makeheaders$E 2>NUL del makeheaders$P 2>NUL del mkversion$E 2>NUL del mkversion$P 2>NUL del codecheck1$E 2>NUL del codecheck1$P 2>NUL del mkbuiltin$E 2>NUL del mkbuiltin$P 2>NUL $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_config$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_dir$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_finfo$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_status$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h $(OX)\add$O : add_.c add.h $(TCC) /Fo$@ -c add_.c add_.c : $(SRCDIR)\add.c translate$E $** > $@ |
︙ | ︙ | |||
529 530 531 532 533 534 535 536 537 538 539 540 541 542 | translate$E $** > $@ $(OX)\browse$O : browse_.c browse.h $(TCC) /Fo$@ -c browse_.c browse_.c : $(SRCDIR)\browse.c translate$E $** > $@ $(OX)\captcha$O : captcha_.c captcha.h $(TCC) /Fo$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c translate$E $** > $@ | > > > > > > > > > > > > > > > > > > | 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 | translate$E $** > $@ $(OX)\browse$O : browse_.c browse.h $(TCC) /Fo$@ -c browse_.c browse_.c : $(SRCDIR)\browse.c translate$E $** > $@ $(OX)\builtin$O : builtin_.c builtin.h $(TCC) /Fo$@ -c builtin_.c builtin_.c : $(SRCDIR)\builtin.c translate$E $** > $@ $(OX)\bundle$O : bundle_.c bundle.h $(TCC) /Fo$@ -c bundle_.c bundle_.c : $(SRCDIR)\bundle.c translate$E $** > $@ $(OX)\cache$O : cache_.c cache.h $(TCC) /Fo$@ -c cache_.c cache_.c : $(SRCDIR)\cache.c translate$E $** > $@ $(OX)\captcha$O : captcha_.c captcha.h $(TCC) /Fo$@ -c captcha_.c captcha_.c : $(SRCDIR)\captcha.c translate$E $** > $@ |
︙ | ︙ | |||
655 656 657 658 659 660 661 662 663 664 665 666 667 668 | translate$E $** > $@ $(OX)\finfo$O : finfo_.c finfo.h $(TCC) /Fo$@ -c finfo_.c finfo_.c : $(SRCDIR)\finfo.c translate$E $** > $@ $(OX)\glob$O : glob_.c glob.h $(TCC) /Fo$@ -c glob_.c glob_.c : $(SRCDIR)\glob.c translate$E $** > $@ | > > > > > > > > > > > > | 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 | translate$E $** > $@ $(OX)\finfo$O : finfo_.c finfo.h $(TCC) /Fo$@ -c finfo_.c finfo_.c : $(SRCDIR)\finfo.c translate$E $** > $@ $(OX)\foci$O : foci_.c foci.h $(TCC) /Fo$@ -c foci_.c foci_.c : $(SRCDIR)\foci.c translate$E $** > $@ $(OX)\fusefs$O : fusefs_.c fusefs.h $(TCC) /Fo$@ -c fusefs_.c fusefs_.c : $(SRCDIR)\fusefs.c translate$E $** > $@ $(OX)\glob$O : glob_.c glob.h $(TCC) /Fo$@ -c glob_.c glob_.c : $(SRCDIR)\glob.c translate$E $** > $@ |
︙ | ︙ | |||
805 806 807 808 809 810 811 812 813 814 815 816 817 818 | translate$E $** > $@ $(OX)\leaf$O : leaf_.c leaf.h $(TCC) /Fo$@ -c leaf_.c leaf_.c : $(SRCDIR)\leaf.c translate$E $** > $@ $(OX)\login$O : login_.c login.h $(TCC) /Fo$@ -c login_.c login_.c : $(SRCDIR)\login.c translate$E $** > $@ | > > > > > > | 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 | translate$E $** > $@ $(OX)\leaf$O : leaf_.c leaf.h $(TCC) /Fo$@ -c leaf_.c leaf_.c : $(SRCDIR)\leaf.c translate$E $** > $@ $(OX)\loadctrl$O : loadctrl_.c loadctrl.h $(TCC) /Fo$@ -c loadctrl_.c loadctrl_.c : $(SRCDIR)\loadctrl.c translate$E $** > $@ $(OX)\login$O : login_.c login.h $(TCC) /Fo$@ -c login_.c login_.c : $(SRCDIR)\login.c translate$E $** > $@ |
︙ | ︙ | |||
901 902 903 904 905 906 907 908 909 910 911 912 913 914 | translate$E $** > $@ $(OX)\printf$O : printf_.c printf.h $(TCC) /Fo$@ -c printf_.c printf_.c : $(SRCDIR)\printf.c translate$E $** > $@ $(OX)\rebuild$O : rebuild_.c rebuild.h $(TCC) /Fo$@ -c rebuild_.c rebuild_.c : $(SRCDIR)\rebuild.c translate$E $** > $@ | > > > > > > > > > > > > | 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 | translate$E $** > $@ $(OX)\printf$O : printf_.c printf.h $(TCC) /Fo$@ -c printf_.c printf_.c : $(SRCDIR)\printf.c translate$E $** > $@ $(OX)\publish$O : publish_.c publish.h $(TCC) /Fo$@ -c publish_.c publish_.c : $(SRCDIR)\publish.c translate$E $** > $@ $(OX)\purge$O : purge_.c purge.h $(TCC) /Fo$@ -c purge_.c purge_.c : $(SRCDIR)\purge.c translate$E $** > $@ $(OX)\rebuild$O : rebuild_.c rebuild.h $(TCC) /Fo$@ -c rebuild_.c rebuild_.c : $(SRCDIR)\rebuild.c translate$E $** > $@ |
︙ | ︙ | |||
955 956 957 958 959 960 961 962 963 964 965 966 967 968 | translate$E $** > $@ $(OX)\shun$O : shun_.c shun.h $(TCC) /Fo$@ -c shun_.c shun_.c : $(SRCDIR)\shun.c translate$E $** > $@ $(OX)\skins$O : skins_.c skins.h $(TCC) /Fo$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c translate$E $** > $@ | > > > > > > | 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 | translate$E $** > $@ $(OX)\shun$O : shun_.c shun.h $(TCC) /Fo$@ -c shun_.c shun_.c : $(SRCDIR)\shun.c translate$E $** > $@ $(OX)\sitemap$O : sitemap_.c sitemap.h $(TCC) /Fo$@ -c sitemap_.c sitemap_.c : $(SRCDIR)\sitemap.c translate$E $** > $@ $(OX)\skins$O : skins_.c skins.h $(TCC) /Fo$@ -c skins_.c skins_.c : $(SRCDIR)\skins.c translate$E $** > $@ |
︙ | ︙ | |||
979 980 981 982 983 984 985 986 987 988 989 990 991 992 | translate$E $** > $@ $(OX)\stat$O : stat_.c stat.h $(TCC) /Fo$@ -c stat_.c stat_.c : $(SRCDIR)\stat.c translate$E $** > $@ $(OX)\style$O : style_.c style.h $(TCC) /Fo$@ -c style_.c style_.c : $(SRCDIR)\style.c translate$E $** > $@ | > > > > > > | 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 | translate$E $** > $@ $(OX)\stat$O : stat_.c stat.h $(TCC) /Fo$@ -c stat_.c stat_.c : $(SRCDIR)\stat.c translate$E $** > $@ $(OX)\statrep$O : statrep_.c statrep.h $(TCC) /Fo$@ -c statrep_.c statrep_.c : $(SRCDIR)\statrep.c translate$E $** > $@ $(OX)\style$O : style_.c style.h $(TCC) /Fo$@ -c style_.c style_.c : $(SRCDIR)\style.c translate$E $** > $@ |
︙ | ︙ | |||
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 | translate$E $** > $@ $(OX)\wikiformat$O : wikiformat_.c wikiformat.h $(TCC) /Fo$@ -c wikiformat_.c wikiformat_.c : $(SRCDIR)\wikiformat.c translate$E $** > $@ $(OX)\winhttp$O : winhttp_.c winhttp.h $(TCC) /Fo$@ -c winhttp_.c winhttp_.c : $(SRCDIR)\winhttp.c translate$E $** > $@ | > > > > > > | 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 | translate$E $** > $@ $(OX)\wikiformat$O : wikiformat_.c wikiformat.h $(TCC) /Fo$@ -c wikiformat_.c wikiformat_.c : $(SRCDIR)\wikiformat.c translate$E $** > $@ $(OX)\winfile$O : winfile_.c winfile.h $(TCC) /Fo$@ -c winfile_.c winfile_.c : $(SRCDIR)\winfile.c translate$E $** > $@ $(OX)\winhttp$O : winhttp_.c winhttp.h $(TCC) /Fo$@ -c winhttp_.c winhttp_.c : $(SRCDIR)\winhttp.c translate$E $** > $@ |
︙ | ︙ | |||
1125 1126 1127 1128 1129 1130 1131 | $(OX)\zip$O : zip_.c zip.h $(TCC) /Fo$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c translate$E $** > $@ fossil.res : $B\win\fossil.rc | | > | > > > | 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 | $(OX)\zip$O : zip_.c zip.h $(TCC) /Fo$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c translate$E $** > $@ fossil.res : $B\win\fossil.rc $(RCC) /fo $@ $** headers: makeheaders$E page_index.h builtin_data.h VERSION.h makeheaders$E add_.c:add.h \ allrepo_.c:allrepo.h \ attach_.c:attach.h \ bag_.c:bag.h \ bisect_.c:bisect.h \ blob_.c:blob.h \ branch_.c:branch.h \ browse_.c:browse.h \ builtin_.c:builtin.h \ bundle_.c:bundle.h \ cache_.c:cache.h \ captcha_.c:captcha.h \ cgi_.c:cgi.h \ checkin_.c:checkin.h \ checkout_.c:checkout.h \ clearsign_.c:clearsign.h \ clone_.c:clone.h \ comformat_.c:comformat.h \ |
︙ | ︙ | |||
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 | diffcmd_.c:diffcmd.h \ doc_.c:doc.h \ encode_.c:encode.h \ event_.c:event.h \ export_.c:export.h \ file_.c:file.h \ finfo_.c:finfo.h \ glob_.c:glob.h \ graph_.c:graph.h \ gzip_.c:gzip.h \ http_.c:http.h \ http_socket_.c:http_socket.h \ http_ssl_.c:http_ssl.h \ http_transport_.c:http_transport.h \ | > > | 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 | diffcmd_.c:diffcmd.h \ doc_.c:doc.h \ encode_.c:encode.h \ event_.c:event.h \ export_.c:export.h \ file_.c:file.h \ finfo_.c:finfo.h \ foci_.c:foci.h \ fusefs_.c:fusefs.h \ glob_.c:glob.h \ graph_.c:graph.h \ gzip_.c:gzip.h \ http_.c:http.h \ http_socket_.c:http_socket.h \ http_ssl_.c:http_ssl.h \ http_transport_.c:http_transport.h \ |
︙ | ︙ | |||
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 | json_report_.c:json_report.h \ json_status_.c:json_status.h \ json_tag_.c:json_tag.h \ json_timeline_.c:json_timeline.h \ json_user_.c:json_user.h \ json_wiki_.c:json_wiki.h \ leaf_.c:leaf.h \ login_.c:login.h \ lookslike_.c:lookslike.h \ main_.c:main.h \ manifest_.c:manifest.h \ markdown_.c:markdown.h \ markdown_html_.c:markdown_html.h \ md5_.c:md5.h \ merge_.c:merge.h \ merge3_.c:merge3.h \ moderate_.c:moderate.h \ name_.c:name.h \ path_.c:path.h \ pivot_.c:pivot.h \ popen_.c:popen.h \ pqueue_.c:pqueue.h \ printf_.c:printf.h \ rebuild_.c:rebuild.h \ regexp_.c:regexp.h \ report_.c:report.h \ rss_.c:rss.h \ schema_.c:schema.h \ search_.c:search.h \ setup_.c:setup.h \ sha1_.c:sha1.h \ shun_.c:shun.h \ skins_.c:skins.h \ sqlcmd_.c:sqlcmd.h \ stash_.c:stash.h \ stat_.c:stat.h \ style_.c:style.h \ sync_.c:sync.h \ tag_.c:tag.h \ tar_.c:tar.h \ th_main_.c:th_main.h \ timeline_.c:timeline.h \ tkt_.c:tkt.h \ tktsetup_.c:tktsetup.h \ undo_.c:undo.h \ unicode_.c:unicode.h \ update_.c:update.h \ url_.c:url.h \ user_.c:user.h \ utf8_.c:utf8.h \ util_.c:util.h \ verify_.c:verify.h \ vfile_.c:vfile.h \ wiki_.c:wiki.h \ wikiformat_.c:wikiformat.h \ winhttp_.c:winhttp.h \ wysiwyg_.c:wysiwyg.h \ xfer_.c:xfer.h \ xfersetup_.c:xfersetup.h \ zip_.c:zip.h \ $(SRCDIR)\sqlite3.h \ $(SRCDIR)\th.h \ VERSION.h \ $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers | > > > > > > | 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 | json_report_.c:json_report.h \ json_status_.c:json_status.h \ json_tag_.c:json_tag.h \ json_timeline_.c:json_timeline.h \ json_user_.c:json_user.h \ json_wiki_.c:json_wiki.h \ leaf_.c:leaf.h \ loadctrl_.c:loadctrl.h \ login_.c:login.h \ lookslike_.c:lookslike.h \ main_.c:main.h \ manifest_.c:manifest.h \ markdown_.c:markdown.h \ markdown_html_.c:markdown_html.h \ md5_.c:md5.h \ merge_.c:merge.h \ merge3_.c:merge3.h \ moderate_.c:moderate.h \ name_.c:name.h \ path_.c:path.h \ pivot_.c:pivot.h \ popen_.c:popen.h \ pqueue_.c:pqueue.h \ printf_.c:printf.h \ publish_.c:publish.h \ purge_.c:purge.h \ rebuild_.c:rebuild.h \ regexp_.c:regexp.h \ report_.c:report.h \ rss_.c:rss.h \ schema_.c:schema.h \ search_.c:search.h \ setup_.c:setup.h \ sha1_.c:sha1.h \ shun_.c:shun.h \ sitemap_.c:sitemap.h \ skins_.c:skins.h \ sqlcmd_.c:sqlcmd.h \ stash_.c:stash.h \ stat_.c:stat.h \ statrep_.c:statrep.h \ style_.c:style.h \ sync_.c:sync.h \ tag_.c:tag.h \ tar_.c:tar.h \ th_main_.c:th_main.h \ timeline_.c:timeline.h \ tkt_.c:tkt.h \ tktsetup_.c:tktsetup.h \ undo_.c:undo.h \ unicode_.c:unicode.h \ update_.c:update.h \ url_.c:url.h \ user_.c:user.h \ utf8_.c:utf8.h \ util_.c:util.h \ verify_.c:verify.h \ vfile_.c:vfile.h \ wiki_.c:wiki.h \ wikiformat_.c:wikiformat.h \ winfile_.c:winfile.h \ winhttp_.c:winhttp.h \ wysiwyg_.c:wysiwyg.h \ xfer_.c:xfer.h \ xfersetup_.c:xfersetup.h \ zip_.c:zip.h \ $(SRCDIR)\sqlite3.h \ $(SRCDIR)\th.h \ VERSION.h \ $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers |
Added win/buildmsvc.bat.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | @ECHO OFF :: :: buildmsvc.bat -- :: :: This batch file attempts to build Fossil using the latest version :: Microsoft Visual Studio installed on this machine. :: :: SETLOCAL REM SET __ECHO=ECHO REM SET __ECHO2=ECHO IF NOT DEFINED _AECHO (SET _AECHO=REM) IF NOT DEFINED _CECHO (SET _CECHO=REM) IF NOT DEFINED _VECHO (SET _VECHO=REM) REM REM NOTE: Setup local environment variables that point to the root directory REM of the Fossil source checkout and to the directory containing this REM build tool. REM SET ROOT=%~dp0\.. SET ROOT=%ROOT:\\=\% %_VECHO% Root = '%ROOT%' SET TOOLS=%~dp0 SET TOOLS=%TOOLS:~0,-1% %_VECHO% Tools = '%TOOLS%' REM REM Visual C++ ???? REM IF DEFINED VCINSTALLDIR IF EXIST "%VCINSTALLDIR%" ( %_AECHO% Build environment appears to be setup. GOTO skip_setupVisualStudio ) REM REM Visual Studio ???? REM IF DEFINED VSVARS32 IF EXIST "%VSVARS32%" ( %_AECHO% Build environment batch file manually overridden to "%VSVARS32%"... GOTO skip_detectVisualStudio ) REM REM Visual Studio 2013 REM IF NOT DEFINED VS120COMNTOOLS GOTO skip_detectVisualStudio2013 SET VSVARS32=%VS120COMNTOOLS%\vsvars32.bat IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2013... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2013 REM REM Visual Studio 2012 REM IF NOT DEFINED VS110COMNTOOLS GOTO skip_detectVisualStudio2012 SET VSVARS32=%VS110COMNTOOLS%\vsvars32.bat IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2012... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2012 REM REM Visual Studio 2010 REM IF NOT DEFINED VS100COMNTOOLS GOTO skip_detectVisualStudio2010 SET VSVARS32=%VS100COMNTOOLS%\vsvars32.bat IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2010... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2010 REM REM Visual Studio 2008 REM IF NOT DEFINED VS90COMNTOOLS GOTO skip_detectVisualStudio2008 SET VSVARS32=%VS90COMNTOOLS%\vsvars32.bat IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2008... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2008 REM REM Visual Studio 2005 REM IF NOT DEFINED VS80COMNTOOLS GOTO skip_detectVisualStudio2005 SET VSVARS32=%VS80COMNTOOLS%\vsvars32.bat IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2005... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2005 REM REM Visual Studio 2003 REM IF NOT DEFINED VS71COMNTOOLS GOTO skip_detectVisualStudio2003 SET VSVARS32=%VS71COMNTOOLS%\vsvars32.bat IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2003... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2003 REM REM Visual Studio 2002 REM IF NOT DEFINED VS70COMNTOOLS GOTO skip_detectVisualStudio2002 SET VSVARS32=%VS70COMNTOOLS%\vsvars32.bat IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2002... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2002 REM REM NOTE: If we get to this point, no Visual Studio build environment batch REM files were found. REM ECHO No Visual Studio build environment batch files were found. GOTO errors REM REM NOTE: At this point, the appropriate Visual Studio version should be REM selected. REM :skip_detectVisualStudio REM REM NOTE: Remove any double-backslash sequences that may be present in the REM selected Visual Studio common tools path. This is not strictly REM necessary; however, it makes reading the output easier. REM SET VSVARS32=%VSVARS32:\\=\% %_VECHO% VsVars32 = '%VSVARS32%' REM REM NOTE: After this point, a clean ERRORLEVEL is required; therefore, make REM sure it is reset now. REM CALL :fn_ResetErrorLevel REM REM NOTE: Attempt to call the selected batch file to setup the environment REM variables for building with MSVC. REM %__ECHO3% CALL "%VSVARS32%" IF ERRORLEVEL 1 ( ECHO Visual Studio build environment batch file "%VSVARS32%" failed. GOTO errors ) REM REM NOTE: After this point, the environment should already be setup for REM building with MSVC. REM :skip_setupVisualStudio %_VECHO% VcInstallDir = '%VCINSTALLDIR%' REM REM NOTE: Attempt to create the build output directory, if necessary. REM IF NOT EXIST "%ROOT%\msvcbld" ( %__ECHO% MKDIR "%ROOT%\msvcbld" IF ERRORLEVEL 1 ( ECHO Could not make directory "%ROOT%\msvcbld". GOTO errors ) ) REM REM NOTE: Attempt to change to the created build output directory so that REM the generated files will be placed there. REM %__ECHO2% PUSHD "%ROOT%\msvcbld" IF ERRORLEVEL 1 ( ECHO Could not change to directory "%ROOT%\msvcbld". GOTO errors ) REM REM NOTE: If requested, setup the build environment to refer to the Windows REM SDK v7.1A, which is required if the binaries are being built with REM Visual Studio 201x and need to work on Windows XP. REM IF DEFINED USE_V110SDK71A ( %_AECHO% Forcing use of the Windows SDK v7.1A... CALL :fn_UseV110Sdk71A ) %_VECHO% Path = '%PATH%' %_VECHO% Include = '%INCLUDE%' %_VECHO% Lib = '%LIB%' %_VECHO% NmakeArgs = '%NMAKE_ARGS%' REM REM NOTE: Attempt to execute NMAKE for the Fossil MSVC makefile, passing REM anything extra from our command line along (e.g. extra options). REM %__ECHO% nmake /f "%TOOLS%\Makefile.msc" %NMAKE_ARGS% %* IF ERRORLEVEL 1 ( GOTO errors ) REM REM NOTE: Attempt to restore the previously saved directory. REM %__ECHO2% POPD IF ERRORLEVEL 1 ( ECHO Could not restore directory. GOTO errors ) GOTO no_errors :fn_UseV110Sdk71A IF "%PROCESSOR_ARCHITECTURE%" == "x86" GOTO set_v110Sdk71A_x86 SET PFILES_SDK71A=%ProgramFiles(x86)% GOTO set_v110Sdk71A_done :set_v110Sdk71A_x86 SET PFILES_SDK71A=%ProgramFiles% :set_v110Sdk71A_done SET PATH=%PFILES_SDK71A%\Microsoft SDKs\Windows\7.1A\Bin;%PATH% SET INCLUDE=%PFILES_SDK71A%\Microsoft SDKs\Windows\7.1A\Include;%INCLUDE% IF "%PLATFORM%" == "x64" ( SET LIB=%PFILES_SDK71A%\Microsoft SDKs\Windows\7.1A\Lib\x64;%LIB% ) ELSE ( SET LIB=%PFILES_SDK71A%\Microsoft SDKs\Windows\7.1A\Lib;%LIB% ) CALL :fn_UnsetVariable PFILES_SDK71A SET NMAKE_ARGS=%NMAKE_ARGS% FOSSIL_ENABLE_WINXP=1 GOTO :EOF :fn_UnsetVariable IF NOT "%1" == "" ( SET %1= CALL :fn_ResetErrorLevel ) GOTO :EOF :fn_ResetErrorLevel VERIFY > NUL GOTO :EOF :fn_SetErrorLevel VERIFY MAYBE 2> NUL GOTO :EOF :usage ECHO. ECHO Usage: %~nx0 [...] ECHO. GOTO errors :errors CALL :fn_SetErrorLevel ENDLOCAL ECHO. ECHO Build failure, errors were encountered. GOTO end_of_file :no_errors CALL :fn_ResetErrorLevel ENDLOCAL ECHO. ECHO Build success, no errors were encountered. GOTO end_of_file :end_of_file %__ECHO% EXIT /B %ERRORLEVEL% |
Added win/fossil.exe.manifest.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="fossil" type="win32" /> <description> Simple, high-reliability, distributed software configuration management system. </description> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <!-- Windows 10 --> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <!-- Windows 8.1 --> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> <!-- Windows 8 --> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <!-- Windows 7 --> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <!-- Windows Vista --> <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> </application> </compatibility> <asmv3:application> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> <dpiAware>true</dpiAware> </asmv3:windowsSettings> </asmv3:application> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> </assembly> |
Changes to win/fossil.rc.
︙ | ︙ | |||
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | # define VS_FF_NONE 0x00000000L #endif /* !defined(VS_FF_NONE) */ #include "VERSION.h" #define _RC_COMPILE_ #include "config.h" #include "sqlite3.h" #include "zlib.h" #if defined(FOSSIL_ENABLE_SSL) #include "openssl/opensslv.h" #endif /* defined(FOSSIL_ENABLE_SSL) */ #if defined(FOSSIL_ENABLE_TCL) #include "tcl.h" | > > > > > > > > > > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | # define VS_FF_NONE 0x00000000L #endif /* !defined(VS_FF_NONE) */ #include "VERSION.h" #define _RC_COMPILE_ #include "config.h" #include "sqlite3.h" #if defined(FOSSIL_ENABLE_MINIZ) #if defined(__MINGW32__) #include "minizver.h" #else #define MINIZ_HEADER_FILE_ONLY #include "miniz.c" #endif /* defined(__MINGW32__) */ #else #include "zlib.h" #endif /* defined(FOSSIL_ENABLE_MINIZ) */ #if defined(FOSSIL_ENABLE_SSL) #include "openssl/opensslv.h" #endif /* defined(FOSSIL_ENABLE_SSL) */ #if defined(FOSSIL_ENABLE_TCL) #include "tcl.h" |
︙ | ︙ | |||
81 82 83 84 85 86 87 | FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Fossil Development Team\0" | | > > > > > > > > > > > > > > | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Fossil Development Team\0" VALUE "FileDescription", "Fossil is a simple, high-reliability, distributed software configuration management system.\0" VALUE "ProductName", "Fossil\0" VALUE "ProductVersion", "Fossil " RELEASE_VERSION " " MANIFEST_VERSION " " MANIFEST_DATE " UTC\0" VALUE "FileVersion", "Fossil " RELEASE_VERSION " " MANIFEST_VERSION " " MANIFEST_DATE " UTC\0" VALUE "InternalName", "fossil\0" VALUE "LegalCopyright", "Copyright © " MANIFEST_YEAR " by D. Richard Hipp. All rights reserved.\0" VALUE "OriginalFilename", "fossil.exe\0" VALUE "CompilerName", COMPILER_NAME "\0" VALUE "SQLiteVersion", "SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\0" #if defined(FOSSIL_ENABLE_MINIZ) VALUE "MinizVersion", "miniz " MZ_VERSION "\0" #else VALUE "ZlibVersion", "zlib " ZLIB_VERSION "\0" #endif /* defined(FOSSIL_ENABLE_MINIZ) */ #if defined(BROKEN_MINGW_CMDLINE) VALUE "CommandLineIsUnicode", "No\0" #else VALUE "CommandLineIsUnicode", "Yes\0" #endif /* defined(BROKEN_MINGW_CMDLINE) */ #if defined(FOSSIL_ENABLE_SSL) VALUE "SslEnabled", "Yes, " OPENSSL_VERSION_TEXT "\0" #endif /* defined(FOSSIL_ENABLE_SSL) */ #if defined(FOSSIL_ENABLE_TH1_DOCS) VALUE "Th1Docs", "Yes\0" #else VALUE "Th1Docs", "No\0" #endif /* defined(FOSSIL_ENABLE_TH1_DOCS) */ #if defined(FOSSIL_ENABLE_TH1_HOOKS) VALUE "Th1Hooks", "Yes\0" #else VALUE "Th1Hooks", "No\0" #endif /* defined(FOSSIL_ENABLE_TH1_HOOKS) */ #if defined(FOSSIL_ENABLE_TCL) VALUE "TclEnabled", "Yes, Tcl " TCL_PATCH_LEVEL "\0" #if defined(USE_TCL_STUBS) VALUE "UseTclStubsEnabled", "Yes\0" #else VALUE "UseTclStubsEnabled", "No\0" #endif /* defined(USE_TCL_STUBS) */ |
︙ | ︙ | |||
128 129 130 131 132 133 134 | END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 0x4b0 END END | > > > > > > > > > > > > > > | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 0x4b0 END END /* * This embedded manifest is needed for Windows 8.1. */ #ifndef RT_MANIFEST #define RT_MANIFEST 24 #endif /* RT_MANIFEST */ #ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID #define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 #endif /* CREATEPROCESS_MANIFEST_RESOURCE_ID */ CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "fossil.exe.manifest" |
Changes to win/include/dirent.h.
︙ | ︙ | |||
18 19 20 21 22 23 24 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < > > > > > | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * $Id: dirent.h,v 1.20 2014/03/19 17:52:23 tronkko Exp $ */ #ifndef DIRENT_H #define DIRENT_H /* * Define architecture flags so we don't need to include windows.h. * Avoiding windows.h makes it simpler to use windows sockets in conjunction * with dirent.h. */ #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_IX86) # define _X86_ #endif #if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && defined(_M_AMD64) #define _AMD64_ #endif #include <stdio.h> #include <stdarg.h> #include <windef.h> #include <winbase.h> |
︙ | ︙ | |||
187 188 189 190 191 192 193 194 195 196 197 198 199 200 | #define DT_UNKNOWN 0 #define DT_REG S_IFREG #define DT_DIR S_IFDIR #define DT_FIFO S_IFIFO #define DT_SOCK S_IFSOCK #define DT_CHR S_IFCHR #define DT_BLK S_IFBLK /* Macros for converting between st_mode and d_type */ #define IFTODT(mode) ((mode) & S_IFMT) #define DTTOIF(type) (type) /* * File type macros. Note that block devices, sockets and links cannot be | > | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | #define DT_UNKNOWN 0 #define DT_REG S_IFREG #define DT_DIR S_IFDIR #define DT_FIFO S_IFIFO #define DT_SOCK S_IFSOCK #define DT_CHR S_IFCHR #define DT_BLK S_IFBLK #define DT_LNK S_IFLNK /* Macros for converting between st_mode and d_type */ #define IFTODT(mode) ((mode) & S_IFMT) #define DTTOIF(type) (type) /* * File type macros. Note that block devices, sockets and links cannot be |
︙ | ︙ | |||
210 211 212 213 214 215 216 | #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) /* Return the exact length of d_namlen without zero terminator */ #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) /* Return number of bytes needed to store d_namlen */ | | | | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) /* Return the exact length of d_namlen without zero terminator */ #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) /* Return number of bytes needed to store d_namlen */ #define _D_ALLOC_NAMLEN(p) (PATH_MAX) #ifdef __cplusplus extern "C" { #endif /* Wide-character version */ struct _wdirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ size_t d_namlen; /* Length of name without \0 */ int d_type; /* File type */ wchar_t d_name[PATH_MAX]; /* File name */ }; typedef struct _wdirent _wdirent; struct _WDIR { struct _wdirent ent; /* Current directory entry */ WIN32_FIND_DATAW data; /* Private file data */ int cached; /* True if data is valid */ |
︙ | ︙ | |||
258 259 260 261 262 263 264 | /* Multi-byte character versions */ struct dirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ size_t d_namlen; /* Length of name without \0 */ int d_type; /* File type */ | | | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | /* Multi-byte character versions */ struct dirent { long d_ino; /* Always zero */ unsigned short d_reclen; /* Structure size */ size_t d_namlen; /* Length of name without \0 */ int d_type; /* File type */ char d_name[PATH_MAX]; /* File name */ }; typedef struct dirent dirent; struct DIR { struct dirent ent; struct _WDIR *wdirp; }; |
︙ | ︙ | |||
420 421 422 423 424 425 426 | /* * Copy file name as wide-character string. If the file name is too * long to fit in to the destination buffer, then truncate file name * to PATH_MAX characters and zero-terminate the buffer. */ n = 0; | | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | /* * Copy file name as wide-character string. If the file name is too * long to fit in to the destination buffer, then truncate file name * to PATH_MAX characters and zero-terminate the buffer. */ n = 0; while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { entp->d_name[n] = datap->cFileName[n]; n++; } dirp->ent.d_name[n] = 0; /* Length of file name excluding zero terminator */ entp->d_namlen = n; |
︙ | ︙ | |||
589 590 591 592 593 594 595 | dirent_set_errno (ENOENT); return NULL; } /* Allocate memory for DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); if (dirp) { | | | < | 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 | dirent_set_errno (ENOENT); return NULL; } /* Allocate memory for DIR structure */ dirp = (DIR*) malloc (sizeof (struct DIR)); if (dirp) { wchar_t wname[PATH_MAX]; size_t n; /* Convert directory name to wide-character string */ error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX); if (!error) { /* Open directory stream using wide-character name */ dirp->wdirp = _wopendir (wname); if (dirp->wdirp) { /* Directory stream opened */ error = 0; |
︙ | ︙ | |||
659 660 661 662 663 664 665 | datap = dirent_next (dirp->wdirp); if (datap) { size_t n; int error; /* Attempt to convert file name to multi-byte string */ error = dirent_wcstombs_s( | | | | < | 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 | datap = dirent_next (dirp->wdirp); if (datap) { size_t n; int error; /* Attempt to convert file name to multi-byte string */ error = dirent_wcstombs_s( &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); /* * If the file name cannot be represented by a multi-byte string, * then attempt to use old 8+3 file name. This allows traditional * Unix-code to access some file names despite of unicode * characters, although file names may seem unfamiliar to the user. * * Be ware that the code below cannot come up with a short file * name unless the file system provides one. At least * VirtualBox shared folders fail to do this. */ if (error && datap->cAlternateFileName[0] != '\0') { error = dirent_wcstombs_s( &n, dirp->ent.d_name, PATH_MAX, datap->cAlternateFileName, PATH_MAX); } if (!error) { DWORD attr; /* Initialize directory entry for return */ entp = &dirp->ent; |
︙ | ︙ | |||
784 785 786 787 788 789 790 | error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; | | | | | > > > | 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 | error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to wide-character string (or count characters) */ n = mbstowcs (wcstr, mbstr, sizeInWords); if (!wcstr || n < count) { /* Zero-terminate output buffer */ if (wcstr && sizeInWords) { if (n >= sizeInWords) { n = sizeInWords - 1; } wcstr[n] = 0; } /* Length of resuting multi-byte string WITH zero terminator */ if (pReturnValue) { *pReturnValue = n + 1; } |
︙ | ︙ | |||
818 819 820 821 822 823 824 | } /* Convert wide-character string to multi-byte string */ static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, | | | | | | > > > | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 | } /* Convert wide-character string to multi-byte string */ static int dirent_wcstombs_s( size_t *pReturnValue, char *mbstr, size_t sizeInBytes, /* max size of mbstr */ const wchar_t *wcstr, size_t count) { int error; #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 or later */ error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); #else /* Older Visual Studio or non-Microsoft compiler */ size_t n; /* Convert to multi-byte string (or count the number of bytes needed) */ n = wcstombs (mbstr, wcstr, sizeInBytes); if (!mbstr || n < count) { /* Zero-terminate output buffer */ if (mbstr && sizeInBytes) { if (n >= sizeInBytes) { n = sizeInBytes - 1; } mbstr[n] = '\0'; } /* Lenght of resulting multi-bytes string WITH zero-terminator */ if (pReturnValue) { *pReturnValue = n + 1; } |
︙ | ︙ | |||
868 869 870 871 872 873 874 | } /* Set errno variable */ static void dirent_set_errno( int error) { | | | 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 | } /* Set errno variable */ static void dirent_set_errno( int error) { #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Microsoft Visual Studio 2005 and later */ _set_errno (error); #else /* Non-Microsoft compiler or older Microsoft compiler */ |
︙ | ︙ |
Changes to win/include/unistd.h.
1 2 3 | #ifndef _UNISTD_H #define _UNISTD_H 1 | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 | #ifndef _UNISTD_H #define _UNISTD_H 1 /* This file intended to serve as a drop-in replacement for * unistd.h on Windows * Please add functionality as neeeded */ #include <stdlib.h> #include <io.h> #define srandom srand #define random rand #if defined(__DMC__) |
︙ | ︙ |
Changes to www/adding_code.wiki.
︙ | ︙ | |||
8 9 10 11 12 13 14 | <h2>2.0 Programming Language</h2> Fossil is written in C-89. There are specific [./style.wiki | style guidelines] that are required for any new code that will be accepted into the Fossil core. But, of course, if you are writing an extension just for yourself, you can use any programming style you want. | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <h2>2.0 Programming Language</h2> Fossil is written in C-89. There are specific [./style.wiki | style guidelines] that are required for any new code that will be accepted into the Fossil core. But, of course, if you are writing an extension just for yourself, you can use any programming style you want. The source code for Fossil is not sent directly into the C compiler. There are three separate code [./makefile.wiki#preprocessing|preprocessors] that run over the code first. 1. The <b>mkindex</b> preprocessor scans all regular source files looking for special comments that contain "help" text and which identify routines that implement specific commands or which generate particular web pages. |
︙ | ︙ | |||
148 149 150 151 152 153 154 | you like (as long as it does not collide with some other symbol in the Fossil code) but it is traditional to name the function "<i>commandname</i><b>_cmd</b>", as is done in the example. You could also use "printf()" instead of "fossil_print()" to generate the output text, if desired. But "fossil_print()" is recommended as it has extra logic to insert \r characters at the right times on | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | you like (as long as it does not collide with some other symbol in the Fossil code) but it is traditional to name the function "<i>commandname</i><b>_cmd</b>", as is done in the example. You could also use "printf()" instead of "fossil_print()" to generate the output text, if desired. But "fossil_print()" is recommended as it has extra logic to insert \r characters at the right times on Windows systems. Once you have the command running, you can then start adding code to make it do useful things. There are lots of utility functions in Fossil for parsing command-line options and for opening and accessing and manipulating the repository and the working check-out. Study implementations of existing commands to get an idea of how things are done. You can easily find the implementations |
︙ | ︙ |
Changes to www/antibot.wiki.
︙ | ︙ | |||
57 58 59 60 61 62 63 | <li> Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0 <li> Mozilla/4.0 (compatible; MSIE 8.0; Windows_NT 5.1; Trident/4.0) <li> Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) <li> Wget/1.12 (openbsd4.9) </ul> The first two UserAgent strings above identify Firefox 19 and | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | <li> Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0 <li> Mozilla/4.0 (compatible; MSIE 8.0; Windows_NT 5.1; Trident/4.0) <li> Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) <li> Wget/1.12 (openbsd4.9) </ul> The first two UserAgent strings above identify Firefox 19 and Internet Explorer 8.0, both running on Windows NT. The third example is the spider used by Google to index the internet. The fourth example is the "wget" utility running on OpenBSD. Thus the first two UserAgent strings above identify the requestor as human whereas the second two identify the requestor as a spider. Note that the UserAgent string is completely under the control of the requestor and so a malicious spider can forge a UserAgent string that makes it look like a human. But most spiders truly |
︙ | ︙ |
Changes to www/branching.wiki.
︙ | ︙ | |||
17 18 19 20 21 22 23 | But as sequential numbers are easier to read, we will substitute them for the 40-character SHA1 hashes in this document. The arrows in figure 1 show the evolution of a project. The initial check-in is 1. Check-in 2 is derived from 1. In other words, check-in 2 was created by making edits to check-in 1 and then committing those edits. We say that 2 is a <i>child</i> of 1 | | | | | | | | | | | | | | | | | | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | But as sequential numbers are easier to read, we will substitute them for the 40-character SHA1 hashes in this document. The arrows in figure 1 show the evolution of a project. The initial check-in is 1. Check-in 2 is derived from 1. In other words, check-in 2 was created by making edits to check-in 1 and then committing those edits. We say that 2 is a <i>child</i> of 1 and that 1 is a <i>parent</i> of 2. Check-in 3 is derived from check-in 2, making 3 a child of 2. We say that 3 is a <i>descendant</i> of both 1 and 2 and that 1 and 2 are both <i>ancestors</i> of 3. <a name="dag"></a> <h2>DAGs</h2> The graph of check-ins is a [http://en.wikipedia.org/wiki/Directed_acyclic_graph | directed acyclic graph] commonly shortened to <i>DAG</i>. Check-in 1 is the <i>root</i> of the DAG since it has no ancestors. Check-in 4 is a <i>leaf</i> of the DAG since it has no descendants. (We will give a more precise definition later of "leaf.") Alas, reality often interferes with the simple linear development of a project. Suppose two programmers make independent modifications to check-in 2. After both changes are committed, the check-in graph looks like figure 2: <center><table border=1 cellpadding=10 hspace=10 vspace=10> <tr><td align="center"> <img src="branch02.gif" width=210 height=140><br> Figure 2 </td></tr></table></center> The graph in figure 2 has two leaves: check-ins 3 and 4. Check-in 2 has two children, check-ins 3 and 4. We call this state a <i>fork</i>. Fossil tries to prevent forks. Suppose two programmers named Alice and Bob are each editing check-in 2 separately. Alice finishes her edits first and commits her changes, resulting in check-in 3. Later, when Bob attempts to commit his changes, fossil verifies that check-in 2 is still a leaf. Fossil sees that check-in 3 has occurred and aborts Bob's commit attempt with a message "would fork." This allows Bob to do a "fossil update" which pulls in Alice's changes, merging them into his own changes. After merging, Bob commits check-in 4 as a child of check-in 3. The result is a linear graph as shown in figure 1. This is how CVS works. This is also how fossil works in [./concepts.wiki#workflow | "autosync"] mode. But perhaps Bob is off-network when he does his commit, so he has no way of knowing that Alice has already committed her changes. Or, it could be that Bob has turned off "autosync" mode in Fossil. Or, maybe Bob just doesn't want to merge in Alice's changes before he has saved his own, so he forces the commit to occur using the "--allow-fork" option to the fossil <b>commit</b> command. For any of these reasons, two commits against check-in 2 have occurred and now the DAG has two leaves. So which version of the project is the "latest" in the sense of having the most features and the most bug fixes? When there is more than one leaf in the graph, you don't really know. So we like to have graphs with a single leaf. To resolve this situation, Alice can use the fossil <b>merge</b> command to merge in Bob's changes in her local copy of check-in 3. Then she can commit the results as check-in 5. This results in a DAG as shown in figure 3. <center><table border=1 cellpadding=10 hspace=10 vspace=10> <tr><td align="center"> <img src="branch03.gif" width=282 height=152><br> Figure 3 </td></tr></table></center> Check-in 5 is a child of check-in 3 because it was created by editing check-in 3. But check-in 5 also inherits the changes from check-in 4 by virtue of the merge. So we say that check-in 5 is a <i>merge child</i> of check-in 4 and that it is a <i>direct child</i> of check-in 3. The graph is now back to a single leaf (check-in 5). We have already seen that if fossil is in autosync mode then Bob would have been warned about the potential fork the first time he tried to commit check-in 4. If Bob had updated his local check-out to merge in Alice's check-in 3 changes, then committed, then the fork would have never occurred. The resulting graph would have been linear, as shown |
︙ | ︙ | |||
110 111 112 113 114 115 116 | taken in figure 1 is better because it is much easier to visualize a linear line of development and because the merging happens automatically instead of as a separate manual step. We will not take sides in that debate. We will simply point out that fossil enables you to do it either way. <h2>Forking Versus Branching</h2> | | | | | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | taken in figure 1 is better because it is much easier to visualize a linear line of development and because the merging happens automatically instead of as a separate manual step. We will not take sides in that debate. We will simply point out that fossil enables you to do it either way. <h2>Forking Versus Branching</h2> Having more than one leaf in the check-in DAG is called a "fork." This is usually undesirable and either avoided entirely, as in figure 1, or else quickly resolved as shown in figure 3. But sometimes, one does want to have multiple leaves. For example, a project might have one leaf that is the latest version of the project under development and another leaf that is the latest version that has been tested. When multiple leaves are desirable, we call this <i>branching</i> instead of <i>forking</i>. Figure 4 shows an example of a project where there are two branches, one for development work and another for testing. <center><table border=1 cellpadding=10 hspace=10 vspace=10> <tr><td align="center"> <img src="branch04.gif" width=426 height=123><br> Figure 4 </td></tr></table></center> The hypothetical scenario of figure 4 is this: The project starts and progresses to a point where (at check-in 2) it is ready to enter testing for its first release. In a real project, of course, there might be hundreds or thousands of check-ins before a project reaches this point, but for simplicity of presentation we will say that the project is ready after check-in 2. The project then splits into two branches that are used by separate teams. The testing team, using the blue branch, finds and fixes a few bugs. This is shown by check-ins 6 and 9. Meanwhile the development team, working on the top uncolored branch, is busy adding features for the second release. Of course, the development team would like to take advantage of the bug fixes implemented by the testing team. So periodically, the changes in the test branch are merged into the dev branch. This is shown by the dashed merge arrows between check-ins 6 and 7 and between check-ins 9 and 10. |
︙ | ︙ | |||
172 173 174 175 176 177 178 | Figure 5 </td></tr></table></center> A <i>tag</i> is a name that is attached to a check-in. A <i>property</i> is a name/value pair. Internally, fossil implements tags as properties with a NULL value. So, tags and properties really are much the same thing, and henceforth we will use the word "tag" | | | | < | | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | Figure 5 </td></tr></table></center> A <i>tag</i> is a name that is attached to a check-in. A <i>property</i> is a name/value pair. Internally, fossil implements tags as properties with a NULL value. So, tags and properties really are much the same thing, and henceforth we will use the word "tag" to mean either a tag or a property. A tag can be a one-time tag, a propagating tag or a cancellation tag. A one-time tag only applies to the check-in to which it is attached. A propagating tag applies to the check-in to which it is attached and also to all direct descendants of that check-in. A <i>direct descendant</i> is a descendant through direct children. Tag propagation does not cross merges. Tag propagation also stops as soon as it encounters another check-in with the same tag. A cancellation tag is attached to a single check-in in order to either override a one-time tag that was previously placed on that same check-in, or to block tag propagation from an ancestor. The initial check-in of every repository has two propagating tags. In figure 5, that initial check-in is check-in 1. The <b>branch</b> tag tells (by its value) what branch the check-in is a member of. The default branch is called "trunk." All tags that begin with "<b>sym-</b>" are symbolic name tags. When a symbolic name tag is attached to a check-in, that allows you to refer to that check-in by its symbolic name rather than by its 40-character SHA1 hash name. When a symbolic name tag propagates (as does the <b>sym-trunk</b> tag) then referring to that name is the same as referring to the most recent check-in with that name. Thus the two tags on check-in 1 cause all descendants to be in the |
︙ | ︙ | |||
210 211 212 213 214 215 216 | so that it cannot reach check-ins 6 or 9. This causes check-ins 4, 6, and 9 to be in the "test" branch and all others to be in the "trunk" branch. Check-in 4 also has a <b>sym-test</b> tag, which gives the symbolic name "test" to check-ins 4, 6, and 9. Because tags do not propagate across merges, check-ins 7, 8, and 10 do not inherit the <b>sym-test</b> tag and are hence not known by the name "test." | | | | | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | so that it cannot reach check-ins 6 or 9. This causes check-ins 4, 6, and 9 to be in the "test" branch and all others to be in the "trunk" branch. Check-in 4 also has a <b>sym-test</b> tag, which gives the symbolic name "test" to check-ins 4, 6, and 9. Because tags do not propagate across merges, check-ins 7, 8, and 10 do not inherit the <b>sym-test</b> tag and are hence not known by the name "test." To prevent the <b>sym-trunk</b> tag from propagating from check-in 1 into check-ins 4, 6, and 9, there is a cancellation tag for <b>sym-trunk</b> on check-in 4. The net effect is that check-ins on the trunk go by the symbolic name of "trunk" and check-ins on the test branch go by the symbolic name "test." The <b>bgcolor=blue</b> tag on check-in 4 causes the background color of timelines to be blue for check-in 4 and its direct descendants. Figure 5 also shows two one-time tags on check-in 9. (The diagram does not make a graphical distinction between one-time and propagating tags.) The <b>sym-release-1.0</b> tag means that check-in 9 can be referred to using the more meaningful name "release-1.0." The <b>closed</b> tag means that check-in 9 is a "closed leaf." A closed leaf is a leaf that should never have direct children. <h2>Review Of Terminology</h2> <blockquote><dl> <dt><b>Branch</b></dt> <dd><p>A branch is a set of check-ins with the same value for their |
︙ | ︙ |
Changes to www/build.wiki.
︙ | ︙ | |||
15 16 17 18 19 20 21 | <h2>0.1 Executive Summary</h2> <p>Building and installing is very simple. Three steps:</p> <ol> <li> Download and unpack a source tarball or ZIP. <li> <b>./configure; make</b> | | | | | > > > > > > > > > > > > > > > > > > > > | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | <h2>0.1 Executive Summary</h2> <p>Building and installing is very simple. Three steps:</p> <ol> <li> Download and unpack a source tarball or ZIP. <li> <b>./configure; make</b> <li> Move the resulting "fossil" or "fossil.exe" executable to someplace on your $PATH. </ol> <p><hr> <h2>1.0 Obtaining The Source Code</h2> <p>Fossil is self-hosting, so you can obtain a ZIP archive or tarball containing a snapshot of the <em>latest</em> version directly from Fossil's own fossil repository. Additionally, source archives of <em>released</em> versions of fossil are available from the <a href="http://www.fossil-scm.org/download.html">downloads page</a>. To obtain a development version of fossil, follow these steps:</p> <ol> <li><p>Point your web browser to <a href="http://www.fossil-scm.org/"> http://www.fossil-scm.org/</a>.</p></li> <li><p>Click on the <a href="http://www.fossil-scm.org/fossil/timeline">Timeline</a> link at the top of the page.</p></li> <li><p>Select a version of of Fossil you want to download. The latest version on the trunk branch is usually a good choice. Click on its link.</p></li> <li><p>Finally, click on one of the "Zip Archive" or "Tarball" links, according to your preference. These link will build a ZIP archive or a gzip-compressed tarball of the complete source code and download it to your computer. </ol> <h2>Aside: Is it really safe to use an unreleased development version of the Fossil source code?</h2> Yes! Any check-in on the [/timeline?t=trunk | trunk branch] of the Fossil [http://fossil-scm.org/fossil/timeline | Fossil self-hosting repository] will work fine. (Dodgy code is always on a branch.) In the unlikely event that you pick a version with a serious bug, it still won't clobber your files. Fossil uses several [./selfcheck.wiki | self-checks] prior to committing any repository change that prevent loss-of-work due to bugs. The Fossil [./selfhost.wiki | self-hosting repositories], especially the one at [http://www.fossil-scm.org/fossil], usually run a version of trunk that is less than a week or two old. Look at the bottom left-hand corner of this screen (to the right of "This page was generated in...") to see exactly which version of Fossil is rendering this page. It is always safe to use whatever version of the Fossil code you find running on the main Fossil website. <h2>2.0 Compiling</h2> <ol> <li value="5"> <p>Unpack the ZIP or tarball you downloaded then <b>cd</b> into the directory created.</p></li> <li><i>(Optional, Unix only)</i> Run <b>./configure</b> to construct a makefile. <ol type="a"> <li><p> If you do not have the OpenSSL library installed on your system, then add <b>--with-openssl=none</b> to omit the https functionality. |
︙ | ︙ | |||
78 79 80 81 82 83 84 | </ol> <li><p>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable. The details depend on your platform and compiler. <ol type="a"> <li><p><i>Unix</i> → the configure-generated Makefile should work on | | | > > | > | > > > > > > > > > > > > > > > > > > > > > | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | </ol> <li><p>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable. The details depend on your platform and compiler. <ol type="a"> <li><p><i>Unix</i> → the configure-generated Makefile should work on all Unix and Unix-like systems. Simply type "<b>make</b>". <li><p><i>Unix without running "configure"</i> → if you prefer to avoid running configure, you can also use: <b>make -f Makefile.classic</b>. You may want to make minor edits to Makefile.classic to configure the build for your system. <li><p><i>MinGW3.x (not 4.0)/MinGW-w64</i> → Use the mingw makefile: "<b>make -f win/Makefile.mingw</b>". On a Windows box you will need either Cygwin or Msys as build environment. On Cygwin, Linux or Darwin you may want to make minor edits to win/Makefile.mingw to configure the cross-compile environment. Hint: don't use MinGW-4.0, it will compile but fossil won't work correctly, see <a href="https://www.fossil-scm.org/index.html/tktview/18cff45a4e210430e24c">https://www.fossil-scm.org/index.html/tktview/18cff45a4e210430e24c</a>. <li><p><i>MSVC</i> → Use the MSVC makefile. First change to the "win/" subdirectory ("<b>cd win</b>") then run "<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to detect and use the latest installed version of MSVC.<br><br>To enable the optional <a href="https://www.openssl.org/">OpenSSL</a> support, first <a href="https://www.openssl.org/source/">download the official source code for OpenSSL</a> and extract it to an appropriately named "<b>openssl-X.Y.ZA</b>" subdirectory within the local [/tree?ci=trunk&name=compat | compat] directory (e.g. "<b>compat/openssl-1.0.2a</b>"), then make sure that some recent <a href="http://www.perl.org/">Perl</a> binaries are installed locally, and finally run one of the following commands: <blockquote><pre> nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin </pre></blockquote> <blockquote><pre> buildmsvc.bat FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin </pre></blockquote> <li><p><i>Cygwin</i> → The same as other Unix-like systems. It is recommended to configure using: "<b>configure --disable-internal-sqlite</b>", making sure you have the "libsqlite3-devel" , "zlib-devel" and "openssl-devel" packages installed first. </ol> </ol> <h2>3.0 Installing</h2> <ol> <li value="8"> <p>The finished binary is named "fossil" (or "fossil.exe" on Windows). Put this binary in a directory that is somewhere on your PATH environment variable. It does not matter where.</p> <li> <p><b>(Optional:)</b> To uninstall, just delete the binary.</p> |
︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 | [http://www.activestate.com/activetcl|ActiveState]. <li><p> To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile generated by configure to add the following lines: <blockquote><pre> TCC += -DSQLITE_WITHOUT_ZONEMALLOC TCC += -DWITHOUT_ICONV TCC += -Dsocketlen_t=int | > > | | 176 177 178 179 180 181 182 183 184 185 186 187 188 | [http://www.activestate.com/activetcl|ActiveState]. <li><p> To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile generated by configure to add the following lines: <blockquote><pre> TCC += -DSQLITE_WITHOUT_ZONEMALLOC TCC += -D_BSD_SOURCE TCC += -DWITHOUT_ICONV TCC += -Dsocketlen_t=int TCC += -DSQLITE_MAX_MMAP_SIZE=0 </pre></blockquote> </ul> |
Changes to www/changes.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <title>Change Log</title> <h2>Changes For Version 1.27 (2013-09-11)</h2> * Enhance the [/help?cmd=changes | fossil changes], [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras], [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands to restrict operation to files and directories named on the command-line. * New --integrate option to [/help?cmd=merge | fossil merge], which automatically closes the merged branch when committing. * Renamed <tt>/stats_report</tt> page to [/reports]. Graph width is now relative, not absolute. * Added <tt>yw=YYYY-WW</tt> (year-week) filter to timeline to limit the results to a specific year and calendar week number, e.g. [/timeline?yw=2013-01]. * Updates to SQLite to prevent opening a repository file using file descriptors | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | <title>Change Log</title> <h2>Changes for Version 1.33 (not released yet)</h2> * Add [/help?cmd=import|fossil import --svn], for importing a subversion repository into fossil which was exported using "svnadmin dump". <h2>Changes for Version 1.32 (2015-03-14)</h2> * When creating a new repository using [/help?cmd=init|fossil init], ensure that the new repository is fully compatible with historical versions of Fossil by having a valid manifest as RID 1. * Anti-aliased rendering of arrowheads on timeline graphs. * Added vi/less-style key bindings to the --tk diff GUI. * Documentation updates to fix spellings and changes all "checkins" to "check-ins". * Add the --repolist option to server commands such as [/help?cmd=server|fossil server] or [/help?cmd=http|fossil http]. * Added the "Xekri" skin. * Enhance the "ln=" query parameter on artifact displays to accept multiple ranges, separate by spaces (or "+" when URL-encoded). * Added [/help?cmd=forget|fossil forget] as an alias for [/help?cmd=rm|fossil rm]. <h2>Changes For Version 1.31 (2015-02-23)</h2> * Change the auxiliary schema by adding columns MLINK.ISAUX and MLINK.PMID columns to the schema, to support better drawing of file change graphs. A [/help?cmd=rebuild|fossil rebuild] is recommended but is not required. so that the new graph drawing logic can work effectively. * Added [/search|search] over Check-in comments, Documents, Tickets and Wiki. Disabled by default. The search can be either a full-scan or it can use an index that is kept up-to-date automatically. The new /srchsetup web-page and the [/help?cmd=fts-config|fts-config] command were added to help configure the search capability. Expect further enhancements to the search capabilities in subsequent releases. * Added form elements to some submenus (in particular the /timeline) for easier operation. * Added the --ifneeded option to [/help?cmd=rebuild|fossil rebuild]. * Added "override skins" using the "skin:" line of the CGI script or using the --skin LABEL option on the [/help?cmd=server|server], [/help?cmd=ui|ui], or [/help?cmd=http|http] commands. * Embedded html documents that begin with <doc class="fossil-doc"> are displayed with standard headers and footers added. * Allow <div style='...'> markup in [/wiki_rules|wiki]. * Renamed "Events" to "Technical Notes", while updating the technote display and control pages. Add support for technotes as plain text or as Markdown. * Added the [/md_rules] pages containing summary instructions on the Markdown format. * Added the --repolist and --nojail options to the various server commands (ex: [/help?cmd=server|fossil server]). * Added the [/help?cmd=all|fossil all add] subcommand to "fossil all". * Improvements to the /login page. Some hyperlinks to pages that require "anonymous" privileges are displayed even if the current user is "nobody" but automatically redirect to /login. * The [/help?cmd=/doc|/doc] web-page will now try to deliver the file "404.md" from the top-level directory (if such a file exists) in place of its built-in 404 text. * Download of Tarballs and ZIP Archives by user "nobody" is now enabled by default in new repositories. * Enhancements to the table sorting controls. More display tables are now sortable. * Add IPv6 support to [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone] * Add more skins such as "San Francisco Modern" and "Eagle". * During shutdown, check to see if the check-out database (".fslckout") contains a lot of free space, and if it does, VACUUM it. * Added the [/mimetype_list] page. * Added the [/hash-collisions] page. * Allow the user of Common Table Expressions in the SQL that defaults ticket reports. * Break out the components (css, footer, and header) for the various built-in skins into separate files in the source tree. <h2>Changes For Version 1.30 (2015-01-19)</h2> * Added the [/help?cmd=bundle|fossil bundle] command. * Added the [/help?cmd=purge|fossil purge] command. * Added the [/help?cmd=publish|fossil publish] command. * Added the [/help?cmd=unpublished|fossil unpublished] command. * Enhance the [/tree] webpage to show the age of each file with the option to sort by age. * Enhance the [/brlist] webpage to show additional information about each branch and to be sortable by clicking on column headers. * Add support for Docker. Just install docker and type "sudo docker run -d -p 8080:8080 nijtmans/fossil" to get it running. * Add the [/help/fusefs|fossil fusefs DIRECTORY] command that mounts a Fuse Filesystem at the given DIRECTORY and populates it with read-only copies of all historical check-ins. This only works on systems that support FuseFS. * Add the administrative log that records all configuration. * Added the [/sitemap] webpage. * Added the [/bloblist] web page. * Let [/help?cmd=new|fossil new] no longer create an initial empty commit by default. The first commit after checking out an empty repository will become the initial commit. * Added the [/help?cmd=all|fossil all dbstat] and [/help?cmd=all|fossil all info] commands. * Update SQLite to version 3.8.8. * Added the --verily option to the [/help?cmd=clean|fossil clean] command. * Add the "autosync-tries" setting to control the number of autosync attempts before returning an error. * Added a compile-time option (--with-miniz) to build using miniz instead of zlib. Disabled by default. * Support customization of commands and webpages, including the ability to add new ones, via the "TH1 hooks" feature. Disabled by default. Enabled via a compile-time option. * Add the <nowiki>[checkout], [render], [styleHeader], [styleFooter], [trace], [getParameter], [setParameter], [artifact], and [globalState]</nowiki> commands to TH1, primarily for use by TH1 hooks. * Automatically adjust the width of command-line timeline output according to the detected width of the terminal. * Prompt the user to optionally fix invalid UTF-8 at check-in. * Added a line-number toggle option to the [/help?cmd=/info|/info] and [/help?cmd=/artifact|/artifact] pages. * Most commands now issue errors rather than silently ignoring unrecognized command-line options. * Use full 40-character SHA1 hashes (instead of abbreviations) in most internal URLs. * The "ssh:" sync method on Windows now uses "plink.exe" instead of "ssh" as the secure-shell client program. * Prevent a partial clone when the connection is lost. * Make the distinction between 301 and 302 redirects. * Allow commits against a closed check-in as long as the commit goes onto a different branch. * Improved cache control in the web interface reduces unnecessary requests for common resources like the page logo and CSS. * Fix a rare and long-standing sync protocol bug that would silently prevent the sync from running to completion. Before this bug-fix it was sometimes necessary to do "fossil sync --verily" to get two repositories in sync. * Add the [/finfo?name=src/foci.c|files_of_checkin] virtual table - useful for ad hoc queries in the [/help?cmd=sqlite3|fossil sql] interface, and also used internally. * Added the "$secureurl" TH1 variable for use in headers and footers. * (Internal:) Add the ability to include resources as separate files in the source tree that are converted into constant byte arrays in the compiled binary. Use this feature to store the Tk script that implements the --tk diff option in a separate file for easier editing. * (Internal:) Implement a system of compile-time checks to help ensure the correctness of printf-style formatting strings. * Fix CVE-2014-3566, also known as the POODLE SSL 3.0 vulnerability. * Numerous documentation fixes and improvements. * Other obscure and minor bug fixes - see the timeline for details. <h2>Changes For Version 1.29 (2014-06-12)</h2> * Add the ability to display content, diffs and annotations for UTF16 text files in the web interface. * Add the "SaveAs..." and "Invert" buttons to the graphical diff display that results from using the --tk option with the [/help/diff | fossil diff] command. * The [/reports] page now requires Read ("o") permissions. The "byweek" report now properly propagates the selected year through the event type filter links. * The [/help/info | info command] now shows leaf status of the checkout. * Add support for tunneling https through a http proxy (Ticket [e854101c4f]). * Add option --empty to the "[/help?cmd=open | fossil open]" command. * Enhanced [/help?cmd=/fileage|the fileage page] to support a glob parameter. * Add -w|--ignore-all-space and -Z|--ignore-trailing-space options to [/help?cmd=annotate|fossil annotate], [/help?cmd=blame|fossil blame], [/help?cmd=diff|fossil (g)diff], [/help?cmd=stash|fossil stash diff]. * Add --strip-trailing-cr option to [/help?cmd=diff|fossil (g)diff] and [/help?cmd=stash|fossil stash diff]. * Add button "Ignore Whitespace" to /annotate, /blame, /ci, /fdiff and /vdiff UI pages. * Enhance [/reports?view=byweekday|/reports] with a "byweekday" view. * Enhance the [/help?cmd=cat|fossil cat] command so that it works outside of a checkout when using the -R command-line option. * Use full-length SHA1 hashes, not abbreviations, in most hyperlinks. * Correctly render the <title> markup on wiki pages in the [/help?cmd=/artifact|/artifact] webpage. * Enhance the [/help?cmd=whatis|fossil whatis] command to report on attachments and cluster artifacts. Added the [/help?cmd=test-whatis-all] command for testing purposes. * Add support for HTTP Basic Authentication on [/help?cmd=clone|clone] and [/help?cmd=sync|sync]. * Fix the [/help?cmd=stash|stash] so that it remembers added files and re-adds them when the stash is applied. * Fix the server so that it avoids writing to the database (and thus avoids database locking issues) on a [/help?cmd=pull|pull] or [/help?cmd=clone|clone]. * Add support for [./server.wiki#loadmgmt|server load management] using both a cache of expensive pages (the [/help?cmd=cache|fossil cache] command) and by rejecting expensive page requests when the server load average is too high. * Add the [/help?cmd=praise|fossil praise] command as an alias for [/help?cmd=blame|fossil blame] for subversion compatibility. * Enhance the [/help?cmd=test-diff|fossil test-diff] command with -y or --tk options so that it shows both filenames above their respective columns in the side-by-side diff output. * Issue a warning if a [/help?cmd=add|fossil add] command tries to add a file that matches the ignore-glob. * Add option -W|--width to "[/help?cmd=stash|fossil stash ls]" and "[/help?cmd=leaves|fossil leaves]" commands. * Enhance support for running as the root user. Now works on Haiku. * Added the <tt>-empty</tt> option to [/help?cmd=new|fossil new], which causes it to not create an initial empty commit. The first commit after checking out a repo created this way will become the initial commit. * Enhance sync operations by committing each round-trip to minimize number of retransmits when autosync fails. Include option for [/help?cmd=update| fossil update] and [/help?cmd=merge| fossil merge] to continue even if missing content. * Minor portability fixes for platforms where the char type is unsigned by default. <h2>Changes For Version 1.28 (2014-01-27)</h2> * Enhance [/help?cmd=/reports | /reports] to support event type filtering. * When cloning a repository, the user name passed via the URL (if any) is now used as the default local admin user's name. * Enhance the SSH transport mechanism so that it runs a single instance of the "fossil" executable on the remote side, obviating the need for a shell on the remote side. Some users may need to add the "?fossil=/path/to/fossil" query parameter to "ssh:" URIs if their fossil binary is not in a standard place. * Add the "[/help?cmd=blame | fossil blame]" command that works just like "fossil annotate" but uses a different output format that includes the user who made each changes and omits line numbers. * Add the "Tarball and ZIP-archive Prefix" configuration parameter under Admin/Configuration. * Fix CGI processing so that it works on web servers that do not supply REQUEST_URI. * Add options --dirsonly, --emptydirs, and --allckouts to the "[/help?cmd=clean | fossil clean]" command. * Ten-fold performance improvement in large "fossil blame" or "fossil annotate" commands. * Add option -W|--width and --offset to "[/help?cmd=timeline | fossil timeline]" and "[/help?cmd=finfo | fossil finfo]" commands. * Option -n|--limit of "[/help?cmd=timeline | fossil timeline]" now specifies the number of entries, just like all other commands which have the -n|--limit option. The various timeline-related functions now output "--- ?? limit (??) reached ---" at the end whenever appropriate. Use "-n 0" if no limit is desired. * Fix handling of password embedded in Fossil URL. * New <tt>--once</tt> option to [/help?cmd=clone | fossil clone] command which does not store the URL or password when cloning. * Modify [/help?cmd=ui | fossil ui] to respect "default user" in an open repository. * Fossil now hides check-ins that have the "hidden" tag in timeline webpages. * Enhance <tt>/ci_edit</tt> page to add the "hidden" tag to check-ins. * Advanced possibilities for commit and ticket change notifications over http using TH1 scripting. * Add --sha1sum and --integrate options to the "[/help?cmd=commit | fossil commit]" command. * Add the "clean" and "extra" subcommands to the "[/help?cmd=all | fossil all]" command * Add the --whatif option to "[/help?cmd=clean|fossil clean]" that works the same as "--dry-run", so that the name does not collide with the --dry-run option of "fossil all". * Provide a configuration option to show dates on the web timeline as "YYMMMDD HH:MM" * Add an option to the "stats" webpage that allows an administrator to see the current repository schema. * Enhancements to the "[/help?cmd=/vdiff|/vdiff]" webpage for more difference display options. * Added the "[/tree?ci=trunk&expand | /tree]" webpage as an alternative to "/dir" and make it the default way of showing file lists. * Send gzipped HTTP responses to clients that support it. <h2>Changes For Version 1.27 (2013-09-11)</h2> * Enhance the [/help?cmd=changes | fossil changes], [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras], [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands to restrict operation to files and directories named on the command-line. * New --integrate option to [/help?cmd=merge | fossil merge], which automatically closes the merged branch when committing. * Renamed <tt>/stats_report</tt> page to [/reports]. Graph width is now relative, not absolute. * Added <tt>yw=YYYY-WW</tt> (year-week) filter to timeline to limit the results to a specific year and calendar week number, e.g. [/timeline?yw=2013-01]. * Updates to SQLite to prevent opening a repository file using file descriptors 1 or 2 on Unix. This fixes a bug under which an assertion failure could overwrite part of a repository database file, corrupting it. * Added support for unlimited line lengths in side-by-side diffs. * New --close option to [/help?cmd=commit | fossil commit], which immediately closes the branch being committed. * Added <tt>chart</tt> option to [/help?cmd=bisect | fossil bisect]. * Improvements to the "human or bot?" determination. * Reports errors about missing CGI-standard environment variables for HTTP |
︙ | ︙ | |||
31 32 33 34 35 36 37 | <h2>Changes For Version 1.26 (2013-06-18)</h2> * The argument to the --port option for the [/help?cmd=ui | fossil ui] and [/help?cmd=server | fossil server] commands can take an IP address in addition to the port number, causing Fossil to bind to just that one IP address. * After prompting for a password, also ask if that password should be remembered. * Performance improvements to the diff engine. | | | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | <h2>Changes For Version 1.26 (2013-06-18)</h2> * The argument to the --port option for the [/help?cmd=ui | fossil ui] and [/help?cmd=server | fossil server] commands can take an IP address in addition to the port number, causing Fossil to bind to just that one IP address. * After prompting for a password, also ask if that password should be remembered. * Performance improvements to the diff engine. * Fix the side-by-side diff engine to work better with multi-byte Unicode text. * Color-coding in the web-based annotation (blame) display. Fix the annotation engine so that it is no longer confused by time-warps. * The markdown formatter is now available by default and can be used for tickets, wiki, and embedded documentation. * Add subcommands "fossil bisect log" and "fossil bisect status" to the [/help?cmd=bisect | fossil bisect] command, as well as other bisect enhancements. * Enhanced defenses that prevent spiders from using excessive CPU and bandwidth. |
︙ | ︙ | |||
53 54 55 56 57 58 59 | letter everywhere. The default value of the "case-sensitive" setting is now FALSE, except when case-sensitivity is enabled in the Windows kernel. See [http://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-casesensitive] * Enhancements to /timeline.rss, adding more flags for filtering results, including the ability to subscribe to changes made to individual tickets. For example: [/timeline.rss?y=t&tkt=12fceeec82]. | | < | | | | | | 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | letter everywhere. The default value of the "case-sensitive" setting is now FALSE, except when case-sensitivity is enabled in the Windows kernel. See [http://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-casesensitive] * Enhancements to /timeline.rss, adding more flags for filtering results, including the ability to subscribe to changes made to individual tickets. For example: [/timeline.rss?y=t&tkt=12fceeec82]. * Improved handling of the differences between case-sensitive and case-insensitive filesystems. * JSON API: added the 'status' command to report local checkout status. * Fixes to the <tt>--args</tt> support and documented this feature in the help. * Added [/stats_report] page. * Added <tt>ym=YYYY-MM</tt> filter to the [/timeline?ym=2013-06]. * Fixed: <tt>config reset</tt> now re-installs default ticket report format. * <tt>ssh://</tt> and <tt>file://</tt> protocols now ignore proxy settings. * Added [/hash-color-test] web page. * Cherry-pick merges are recorded internally (though no yet displayed on the timeline graph.) * Bring in the latest versions of SQLite, zlib, and autosetup from upstream. <h2>Changes For Version 1.25 (2013-02-16)</h2> * Enhancements to ticket processing. There are now two tables: TICKET and TICKETCHNG. There is one row in TICKETCHNG for each ticket artifact. Fields from ticket artifacts go into either or both of TICKET and TICKETCHNG, whichever contain matching column names. Default ticket edit and viewing scripts are updated to use TICKETCHNG. The TH1 scripting language is enhanced to support this, including the new "query" command for doing SQL queries against the repository database. All changes should be backwards compatible. * Add the ability to moderate ticket and wiki changes. Unmoderated changes do not sync and may be deleted by the moderator if found to contain spam or other objectionable content. * Add javascript so that clicking on a node of the timeline graph selects that node. Then clicking on a second node shows a diff between the two nodes. Clicking on the selected node unselects it. * Warn of unresolved merge conflicts in "fossil status" and disallow commits of unresolved conflicts unless the --allow-conflict option is used. * Add javascript so that clicking on column headers in a ticket report sorts by the indicated column. * Add the "fossil cat" command which is basically an alias for "fossil finfo -p". * Hyperlinks with the class "button" are rendered as submenu buttons on embedded documentation. * The check-in comment editor on Windows now defaults to NotePad.exe. * Correctly deal with BOMs in check-in comments. Also attempt to convert check-in comments to UTF8 from other encodings. * Allow the deletion of multiple stash entries using multiple arguments to the "fossil stash rm" command. * Enhance the "fossil server DIRECTORY" command to serve static content files contained in DIRECTORY. For security, only files with a recognized suffix (such as *.html, *.jpg, *.txt, etc) will be delivered as static content, and *.fossil files are not on the list of recognized suffixes. There are additional restrictions on the names of the files. * Allow the "fossil ui" command to specify a directory as long as the the --notfound option is used. * Add a configuration option that causes timeline messages to be rendered as text/x-fossil-plain (which is the same as text/plain except that |
︙ | ︙ | |||
124 125 126 127 128 129 130 | dry-run merge. Display an improved merge-summary message at the end of the merge. * Add options to "fossil commit" to override the various sanity checks. Options added: --allow-empty, --allow-fork, --allow-older, and --allow-conflict. * Optionally require a CAPTCHA (controlled by a setting on the Admin/Access webpage) when a user who is not logged in tries to | | | | | | | | | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | dry-run merge. Display an improved merge-summary message at the end of the merge. * Add options to "fossil commit" to override the various sanity checks. Options added: --allow-empty, --allow-fork, --allow-older, and --allow-conflict. * Optionally require a CAPTCHA (controlled by a setting on the Admin/Access webpage) when a user who is not logged in tries to edit wiki, or a ticket, or an attachment. * Improvements to the "ssh://" sync protocol, to help it move past noisy motd comments. * Add the uf=FILE-SHA1-HASH query parameter to the timeline, causing the timeline to show only check-ins that contain the specific file identified by FILE-SHA1-HASH. ("uf" stands for "uses file".) * Enhance the file change annotator so that it follows the file across name changes. * Fix the server-side of the sync protocol so that it will not generate a delta loop when a file changes from its original state, through two or more intermediate states, and back to the original state, all within a single sync. * Show much less output during a sync operation, unless the --verbose option is used. * Set the action= attribute of <form> elements using javascript, as an addition defense against spam-bots. * Disallow invalid UTF8 characters (such as characters in the surrogate pair range) in filenames. * Judge the UserAgent strings issued by the NetSurf webbrowser to be coming from a human, not from a bot. * Add the zlib sources to the Fossil source tree (under compat/zlib) and use those sources when compiling on (Windows) systems that do not have a zlib library installed by default. * Prompt the user with the option to convert non-UTF8 files into UTF8 when committing. * Allow the characters <nowiki>*[]?</nowiki> in filenames. * Allow the --context option on diff commands to have a value of 0. * Added the "dbstat" command. * Enhanced "fossil merge" so that if the VERSION argument is omitted, Fossil tries to merge any forks of the current branch. * Improved detection of forks in a commit race. * Added the --analyze option to "fossil rebuild". <h2>Changes For Version 1.24 (2012-10-22)</h2> * Added support for WYSIWYG editing of wiki pages. WYSIWYG is turned off by default and can be turned on by setting a configuration option. * Allow style= attribute to occur in HTML markup on wiki pages. * Added the --tk option to the "fossi diff" and "fossil stash diff" commands, causing color-coded diff output to be displayed in a Tcl/Tk GUI window. This option only works if Tcl/Tk is installed on the host. * On Windows, make the "gdiff" command default to use WinDiff.exe. * Update the "fossil stash" command so that it always prompts for a comment if the -m option is omitted. * Enhance the timeline webpages so that a=, b=, c=, d=, p=, and dp= query parameters (and others) can all accept any valid check-in name (such as branch names or labels) instead of just SHA1 hashes. * Added the "fossil stash show" command. * Added the "fileage" webpage with links to this page from the check-in information page and from the file browser. * Added --age and -t options to the "fossil ls" command. * Added the --setmtime option to "fossil update". When used, the mtime of all managed files is set to the time when the most recent version of the file was checked in. * Changed the "vdiff" webpage to show the complete text of files that were added or removed (the equivalent of using the -N or --newfile options with the "fossil diff" command-line.) * Added the --temp option to "fossil clean" and "fossil extra", causing those commands to only look at temporary files generated by Fossil, such as merge-conflict reports or aborted check-in messages. * Enhance the raw page download so that it can guess the mimetype of attachments based on the filename. * Change the behavior of the from= and to= query parameters on the |
︙ | ︙ | |||
229 230 231 232 233 234 235 | to use Tcl as part of their configuration. This reduces the size of the Fossil binary and allows any version of Tcl 8.4 or later to be used. * Merge the latest SQLite changes from upstream. * Lots of minor bug fixes. <h2>Changes For Version 1.23 (2012-08-08)</h2> * The default checkout database name is now ".fslckout" instead of | | | | | | | | | | | | | | | | < | | | < | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 | to use Tcl as part of their configuration. This reduces the size of the Fossil binary and allows any version of Tcl 8.4 or later to be used. * Merge the latest SQLite changes from upstream. * Lots of minor bug fixes. <h2>Changes For Version 1.23 (2012-08-08)</h2> * The default checkout database name is now ".fslckout" instead of "_FOSSIL_" on Unix. Both names continue to work. * Added the "fossil all changes" command * Added the --ckout option to the "fossil all list" command * Added the "public-pages" glob pattern that can be configured to allow anonymous users to see embedded documentation on sites where source code should not be accessible to anonymous users. * Allow multiple --tag options on the same "fossil commit" command. * Change the meaning of the --bgcolor option for "fossil commit" to only change the color for that one commit. The new --branchcolor option is available to set a persistent background color. * Add the branch= query parameter to the vdiff page and the --branch option to the "fossil diff" command. * Check-in names of the form "root:BRANCH" now refer to the origin of the branch. Hence to see all changes in a branch, use "fossil diff --from root:BRANCH --to BRANCH". The --branch option on the diff command is an alias for the same. * Add the ability to configure ad-units to be displayed between the menu bar and the content. * Add the ability to set a background image as part of server configuration. * Allow partial commits of cherrypick merges. * Updates against an uncommitted merge are now a warning, not a fatal error. * Prompt the user to continue if a check-in comment is unedited. * Fixes to case sensitivity settings with the /dir webpage. * Repositories now try to remember the locations of all checkouts and web-access URLs and display this information with the "fossil info $REPO" command. * Improved defense against spiders: The src= attribute of <a> elements is set using javascript after the page loads. * Enhanced formatting of the user list page. * If a file named in "fossil add" is missing, that is now a warning instead of a fatal error. * Fix side-by-side diff so that it displays correctly with multi-byte UTF8 characters. * Performance improvements in the diff logic. * Other performance tweaks and documentation updates. <h2>Changes For Version 1.22 (2012-03-17)</h2> * Greatly improved "diff" processing including the new --brief option, partial line matching, colorized in-line diffs, and better performance. * Promote "allow-symlinks" to a versionable setting * Harden the CGI processing logic against DOS attacks * Add the ability to run TH1 scripts after sync requests * Store the repository name in _FOSSIL_ as it is type in the "open" command, possibly as a relative pathname. * Make ".fslckout" the alternative name for the "_FOSSIL_" file. * Change the "ssh:" transfer method to allow all access regardless of user permission. * Improvements to the timeline messages associated with tag changes. (Requires a "[/help/rebuild | fossil rebuild]" to take effect.) * Various additions and fixes for the JSON API. * Improved merge-with-rename handling. * --cherrypick merges use their origin's commit message by default. * Added support for multiple concurrent logins per user. * Update to use SQLite version 3.7.11. * Various minor bug fixes. <h2>Changes For Version 1.21 (2011-12-13)</h2> * Added side-by-side diffs in the command-line interface * Automatically enable hyperlinks if the UserAgent string in the HTTP header suggests that the requestor is a human and not a bot. * Show only commonly used commands with "fossil help". Use "fossil help --all" to see the complete list now. * Improvements to the "stash" command: (1) Stash all files, not just those below the working directory. (2) Add the --detail option to "list". (3) Confirm before "drop --all". (4) Add the "help" subcommand. * Add an Admin/Access setting to change the number of octets of the IP address that are saved in login cookies - allowing this setting to be changed to zero * Promote the "test-md5sum" command to "md5sum". * Added the "whatis" command. * Stop showing the server-code in status outputs - it is no longer used for anything. * Added a compile-time option (--with-tcl) to build in full Tcl scripting support via integration with TH1. * Merged the JSON branch into trunk. Disabled by default. Enabled by a compile-time option. Probably it will be enabled by default in some future release. * Update to use SQLite version 3.7.9 plus the alignment fix for Sparc. align <h2>Changes For Version 1.20 (2011-10-21)</h2> * Added side-by-side diffs in HTML interface. [0bde74ea1e] * Added support for symlinks. (Controlled by "allow-symlinks" setting, off by default). [e4f1c1fe95] * Fixed CLI annotate to show the proper file version in case there are multiple equal versions in history. [e161670939] * Timeline now shows tag changes (requires rebuild).[87540ed6e6] * Fixed annotate to show "more relevant" versions of lines in some cases. [e161670939] * New command: ticket history. [98a855c508] * Disabled SSLv2 in HTTPS client.[ea1d369d23] * Fixed constant prompting regarding previously-saved SSL certificates. [636804745b] * Other SSL improvements. * Added -R REPOFILE support to several more CLI commands. [e080560378] * Generated tarballs now have constant timestamps, so they are always identical for any given check-in. [e080560378] * A number of minor HTML-related tweaks and fixes. * Added --args FILENAME global CLI argument to import arbitrary CLI arguments from a file (e.g. long file lists). [e080560378] * Fixed significant memory leak in annotation of files with long histories.[9929bab702] * Added warnings when a merge operation overwrites local copies (UNDO is available, but previously this condition normally went silently unnoticed). [39f979b08c] * Improved performance when adding many files. [a369dc7721] * Improve merges which contain many file renames. [0b93b0f958] * Added protection against timing attacks. [d4a341b49d] * Firefox now remembers filled fields when returning to forms. [3fac77d7b0] * Added the --stats option to the rebuild command. [f25e5e53c4] * RSS feed now passes validation. [ce354d0a9f] * Show overridden user when entering commit comment. [ce354d0a9f] * Made rebuilding from web interface silent. [ce354d0a9f] * Now works on MSVC with repos >2GB. [6092935ff2] * A number of code cleanups to resolve warnings from various compilers. * Update the built-in SQLite to version 3.7.9 beta. <h2>Changes For Version 1.19 (2011-09-02)</h2> * Added a ./configure script based on autosetup. * Added the "[/help/winsrv | fossil winsrv]" command for creating a Fossil service on Windows systems. * Added "versionable settings" where settings that affect the local tree can be stored in versioned files in the .fossil-settings directory. * Background colors for branches are chosen automatically if no color is specified by the user. * The status, changes and extras commands now show pathnames relative to the current working directory, unless overridden by command line options or the "relative-paths" setting.<br><b>WARNING:</b> This change will break scripts which rely on the current output when the current working directory is not the repository root. * Added "empty-dirs" versionable setting. * Added support for client-side SSL certificates with "ssl-identity" setting and --ssl-identity option. * Added "ssl-ca-location" setting to specify trusted root SSL certificates. * Added the --case-sensitive BOOLEAN command-line option to many commands. Default to true for Unix and false for Windows. * Added the "Color-Test" submenu button on the branch list web page. * Compatibility improvements to the git-export feature. * Performance improvements on SHA1 checksums * Update to the latest SQLite version 3.7.8 alpha. * Fix the tarball generator to work with very log pathnames <h2>Changes For Version 1.18 (2011-07-14)</h2> * Added this Change Log * Added sequential version numbering * Added a optional configure script - the Makefile still works for most systems. * Improvements to the "annotate" algorithm: only search primary ancestors and ignore branches. * Update the "scrub" command to remove traces of login-groups and |
︙ | ︙ |
Changes to www/checkin_names.wiki.
︙ | ︙ | |||
41 42 43 44 45 46 47 | determines which version of the documentation to display. Fossil provides a variety of ways to specify a check-in. This document describes the various methods. <h2>Canonical Check-in Name</h2> | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | determines which version of the documentation to display. Fossil provides a variety of ways to specify a check-in. This document describes the various methods. <h2>Canonical Check-in Name</h2> The canonical name of a check-in is the SHA1 hash of its [./fileformat.wiki#manifest | manifest] expressed as a 40-character lowercase hexadecimal number. For example: <blockquote><pre> fossil info e5a734a19a9826973e1d073b49dc2a16aa2308f9 </pre></blockquote> |
︙ | ︙ | |||
113 114 115 116 117 118 119 120 121 122 123 124 125 126 | fossil info tag:deed2 </tt></blockquote> The "tag:deed2" name will refer to the most recent check-in tagged with "deed2" not to the check-in whose canonical name begins with "deed2". <h2>Timestamps</h2> A timestamp in one of the formats shown below means the most recent check-in that occurs no later than the timestamp given: * <i>YYYY-MM-DD</i> * <i>YYYY-MM-DD HH:MM</i> | > > > > > > > > > > > > > > > > > > > > > | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | fossil info tag:deed2 </tt></blockquote> The "tag:deed2" name will refer to the most recent check-in tagged with "deed2" not to the check-in whose canonical name begins with "deed2". <h2>Whole Branches</h2> Usually when a branch name is specified, it means the latest check-in on that branch. But for some commands (ex: [/help/purge|purge]) a branch name on the argument means the earliest connected check-in on the branch. This seems confusing when being explained here, but it works out to be intuitive in practice. For example, the command "fossil purge XYZ" means to purge the check-in XYZ and all of its descendants. But when XYZ is in the form of a branch name, one generally wants to purge the entire branch, not just the last check-in on the branch. And so for this reason, commands like purge will interpret a branch name to be the first check-in of the branch rather than the last. If there are two or more branches with the same name, then these commands will select the first check-in of the branch that has the most recent check-in. What happens is that Fossil searches for the most recent check-in with the given tag, just as it always does. But if that tag is a branch name, it then walks back down the branch looking for the first check-in of that branch. Again, this behavior only occurs on a few commands where it make sense. <h2>Timestamps</h2> A timestamp in one of the formats shown below means the most recent check-in that occurs no later than the timestamp given: * <i>YYYY-MM-DD</i> * <i>YYYY-MM-DD HH:MM</i> |
︙ | ︙ | |||
136 137 138 139 140 141 142 | In its default configuration, Fossil interprets and displays all dates in Universal Coordinated Time (UTC). This tends to work the best for distributed projects where participants are scattered around the globe. But there is an option on the Admin/Timeline page of the web-interface to switch to local time. The "<b>Z</b>" suffix on an timestamp check-in name is meaningless if Fossil is in the default mode of using UTC for | | | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | In its default configuration, Fossil interprets and displays all dates in Universal Coordinated Time (UTC). This tends to work the best for distributed projects where participants are scattered around the globe. But there is an option on the Admin/Timeline page of the web-interface to switch to local time. The "<b>Z</b>" suffix on an timestamp check-in name is meaningless if Fossil is in the default mode of using UTC for everything, but if Fossil has been switched to local time mode, then the "<b>Z</b>" suffix means to interpret that particular timestamp using UTC instead of local time. For an example of how timestamps are useful, consider the homepage for the Fossil website itself: <blockquote> http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki </blockquote> |
︙ | ︙ |
Changes to www/concepts.wiki.
︙ | ︙ | |||
77 78 79 80 81 82 83 | at a repository and get human-readable status, history, and tracking information about the project. <h3>2.1 Identification Of Artifacts</h3> A particular version of a particular file is called an "artifact". Each artifact has a universally unique name which is the | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | at a repository and get human-readable status, history, and tracking information about the project. <h3>2.1 Identification Of Artifacts</h3> A particular version of a particular file is called an "artifact". Each artifact has a universally unique name which is the <a href="http://en.wikipedia.org/wiki/SHA1">SHA1</a> hash of the content of that file expressed as 40 characters of lower-case hexadecimal. Such a hash is referred to as the Artifact Identifier or Artifact ID for the artifact. The SHA1 algorithm is created with the purpose of providing a highly forgery-resistant identifier for a file. Given any file it is simple to find the artifact ID for that file. But given a artifact ID it is computationally intractable to generate a file that will have that Artifact ID. |
︙ | ︙ | |||
122 123 124 125 126 127 128 | order and it can figure out the relationship between those artifacts and reconstruct the complete development history of a software project. <h3>2.2 Manifests</h3> Associated with every check-in is a special file called the | > | | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | order and it can figure out the relationship between those artifacts and reconstruct the complete development history of a software project. <h3>2.2 Manifests</h3> Associated with every check-in is a special file called the [./fileformat.wiki#manifest| "manifest"]. The manifest is a listing of all other files in that source tree. The manifest contains the (complete) artifact ID of the file and the name of the file as it appears on disk, and thus serves as a mapping from artifact ID to disk name. The artifact ID of the manifest is the identifier for the entire check-in. When you look at a "timeline" of changes in fossil, the ID associated with each check-in or commit is really just the artifact ID of the manifest for that check-in. |
︙ | ︙ | |||
175 176 177 178 179 180 181 | every artifact in a check-in.</li> <li>The artifact ID of the manifest is the identifier of the check-in.</li> </ul> <h2>3.0 Fossil - The Program</h2> Fossil is software. The implementation of fossil is in the form | | | > > > > | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | every artifact in a check-in.</li> <li>The artifact ID of the manifest is the identifier of the check-in.</li> </ul> <h2>3.0 Fossil - The Program</h2> Fossil is software. The implementation of fossil is in the form of a single executable named "fossil" (or "fossil.exe" on Windows). To install fossil on your system, all you have to do is obtain a copy of this one executable file (either by downloading a <a href="http://www.fossil-scm.org/download.html">pre-compiled version</a> or [./build.wiki | compiling it yourself]) and then putting that file somewhere on your PATH. Fossil is completely self-contained. It is not necessary to install any other software in order to use fossil. You do <u>not</u> need CVS, gzip, diff, rsync, Python, Perl, Tcl, Java, apache, PostgreSQL, MySQL, SQLite, patch, or any similar software on your system in order to use fossil effectively. You will want to have some kind of text editor for entering check-in comments. Fossil will use whatever text editor is identified by your VISUAL environment variable. Fossil will also use GPG to clearsign your manifests if you happen to have it installed, but fossil will skip that step if GPG missing from your system. You can optionally set up fossil to use external "diff" programs, though fossil has an excellent built-in "diff" algorithm that works fine for most people. If you happen to have Tcl/Tk installed on your system, Fossil will use it to generate a graphical "diff" display when you use the --tk option to the "diff" command, but this too is entirely optional. To uninstall fossil, simply delete the executable. To upgrade an older version of fossil to a newer version, just replace the old executable with the new one. You might need to run "<b>fossil all rebuild</b>" to restructure your repositories after an upgrade. Running "all rebuild" never hurts, so when upgrading it |
︙ | ︙ | |||
366 367 368 369 370 371 372 | to pull those changes into your local repository. Note that <b>pull</b> does not move the changes into your local source tree, only into your local repository. </li> <li> Once changes are in your local repository, use | | | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | to pull those changes into your local repository. Note that <b>pull</b> does not move the changes into your local source tree, only into your local repository. </li> <li> Once changes are in your local repository, use the <b>update</b> command to merge them to your local source tree. If you merge in some changes and find that the changes do not work out or are not to your liking, you can back out the changes using the <b>undo</b> command. </li> <li> If two or more people ran "commit" against the same check-in, this will |
︙ | ︙ | |||
388 389 390 391 392 393 394 | </ol> <h2>5.0 Setting Up A Fossil Server</h2> With other configuration management software, setting up a server is a lot of work and normally takes time, patience, and a lot of system knowledge. Fossil is designed to avoid this frustration. Setting up | | | | < < < < < < < < | < < < < < < < < < < | | < < < < | < < < < | < < | < | < | < < < | < | | < < < < < < < < < > | < | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | </ol> <h2>5.0 Setting Up A Fossil Server</h2> With other configuration management software, setting up a server is a lot of work and normally takes time, patience, and a lot of system knowledge. Fossil is designed to avoid this frustration. Setting up a server with fossil is ridiculously easy. You have four options:</p> <ol> <li><p><b>Stand-alone server.</b> Simply run the [/help?cmd=server|fossil server] or [/help?cmd=ui|fossil ui] command from the command-line. <li><p><b>CGI.</b> Install a 2-line CGI script on a CGI-enabled web-server like Apache. <li><p><b>SCGI.</b> Start an SCGI server using the [/help?cmd=server| fossil server --scgi] command for handling SCGI requests from web-servers like Nginx. <li><p><b>Inetd or Stunnel.</b> Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests directly to the [/help?cmd=http|fossil http] command. </ol> See the [./server.wiki | How To Configure A Fossil Server] document for details. <h2>6.0 Review Of Key Concepts</h2> <ul> <li>The <b>fossil</b> program is a self-contained stand-alone executable. Just put it somewhere on your PATH to install it.</li> <li>Use the <b>clone</b> or <b>new</b> commands to create a new repository.</li> |
︙ | ︙ |
Changes to www/contribute.wiki.
︙ | ︙ | |||
13 14 15 16 17 18 19 | the introduction of code with incompatible licenses or other entanglements that might cause legal problems for Fossil users. Many larger companies and other lawyer-rich organizations require this as a precondition to using Fossil. If you do not wish to submit a Contributor Agreement, we would still welcome your suggestions and example code, but we will not use your code | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | the introduction of code with incompatible licenses or other entanglements that might cause legal problems for Fossil users. Many larger companies and other lawyer-rich organizations require this as a precondition to using Fossil. If you do not wish to submit a Contributor Agreement, we would still welcome your suggestions and example code, but we will not use your code directly - we will be forced to re-implement your changes from scratch which might take longer. <h2>2.0 Submitting Patches</h2> Suggested changes or bug fixes can be submitted by creating a patch against the current source tree. Email patches to <a href="mailto:drh@sqlite.org">drh@sqlite.org</a>. Be sure to |
︙ | ︙ | |||
49 50 51 52 53 54 55 | A contributor agreement is, of course, a prerequisite for check-in privileges.</p> Contributors are asked to make all non-trivial changes on a branch. The Fossil Architect (Richard Hipp) will merge changes onto the trunk.</p> Contributors are required to following the | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | A contributor agreement is, of course, a prerequisite for check-in privileges.</p> Contributors are asked to make all non-trivial changes on a branch. The Fossil Architect (Richard Hipp) will merge changes onto the trunk.</p> Contributors are required to following the [./checkin.wiki | pre-checkin checklist] prior to every check-in to the Fossil self-hosting repository. This checklist is short and succinct and should only require a few seconds to follow. Contributors should print out a copy of the pre-checkin checklist and keep it on a notecard beside their workstations, for quick reference. Contributors should review the [./style.wiki | Coding Style Guidelines] and mimic the coding style |
︙ | ︙ |
Changes to www/copyright-release.html.
1 2 3 4 5 6 7 | <h1 align="center"> Fossil SCM Contributor Agreement </h1> <p> This agreement applies to your contribution of material to the Fossil Software Configuration Management System ("Fossil") that is | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <h1 align="center"> Fossil SCM Contributor Agreement </h1> <p> This agreement applies to your contribution of material to the Fossil Software Configuration Management System ("Fossil") that is managed by Hipp, Wyrick & Company, Inc. ("Hwaci") and sets out the intellectual property rights you grant to Hwaci in the contributed material. The terms "contribution" and "contributed material" mean any source code, object code, patch, tool, sample, graphic, specification, manual, documentation, or any other material posted, submitted, or uploaded by you to the Fossil project. The term "you" means the person identified |
︙ | ︙ | |||
64 65 66 67 68 69 70 | <ul> <li> Your contribution is an original work and that you can legally grant the rights set out in this agreement. <li> Your contribution does not, to the best of your knowledge and belief, violate any third party's copyrights, trademarks, patents, or other intellectual property rights. <li> You are authorized to sign this agreement on behalf of your | | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <ul> <li> Your contribution is an original work and that you can legally grant the rights set out in this agreement. <li> Your contribution does not, to the best of your knowledge and belief, violate any third party's copyrights, trademarks, patents, or other intellectual property rights. <li> You are authorized to sign this agreement on behalf of your company (if applicable). </ul> </ol> <p>By filling in the following information and signing your name, you agree to be bound by all of the terms set forth in this agreement. Please print clearly.</p> |
︙ | ︙ |
Added www/customskin.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | Theming ======= Every HTML page generated by Fossil has the following basic structure: <blockquote><table border=1 cellpadding=10><tbody> <tr><td style='background-color:lightblue;text-align:center;'>Header</td></tr> <tr><td style='background-color:lightgreen;text-align:center;'> Fossil-Generated Content</td></tr> <tr><td style='background-color:lightblue;text-align:center;'>Footer</td></tr> </tbody></table></blockquote> The header and footer control the "look" of Fossil pages. Those two sections can be customized separately for each repository to develop a new theme. The header will normally look something like this: <html> <head> ... </head> <body> ... top banner and menu bar ... <div class='content'> And the footer will look something like this: </div> ... bottom material ... </body> </html> The <head> element in the header will normally reference the /style.css CSS file that Fossil stores internally. (The $stylesheet_url TH1 variable, described below, is useful for accomplishing this.) The middle "content" section comprised the bulk of most pages and contains the actual Fossil-generated data that the user is interested in seeing. The text of this content section is not normally configurable. The content text can be styled using CSS, but it otherwise fixed. Hence it is the header and footer and the CSS that determine the look of a repository. We call the bundle of built-in CSS, header, and footer a "skin". Built-in Skins -------------- Fossil comes with several built-in skins. The sources to these built-ins can be found in the Fossil source tree under the skins/ folder. The skins/ folder contains a separate subfolder for each built-in skin, with each subfolders holding four files, "css.txt", "details.txt", "footer.txt", and "header.txt", that describe the CSS, rendering options, footer, and header for that skin, respectively. The skin of a repository can be changed to any of the built-in skins using the web interface by going to the /setup_skin web page (requires Admin privileges) and clicking the appropriate button. Or, the --skin command line option can be used for the [fossil ui](../../../help?cmd=ui) or [fossil server](../../../help?cmd=server) commands to force that particular instance of Fossil to use the specified built-in skin. Sharing Skins ------------- The skin of a repository is not part of the versioned state and does not "push" or "pull" like checked-in files. The skin is local to the repository. However, skins can be shared between repositories using the [fossil config](../../../help?cmd=configuration) command. The "fossil config push skin" command will send the local skin to a remote repository and the "fossil config pull skin" command will import a skin from a remote repository. The "fossil config export skin FILENAME" will export the skin for a repository into a file FILENAME. This file can then be imported into a different repository using the "fossil config import FILENAME" command. Unlike "push" and "pull", the "export" and "import" commands are able to move skins between repositories for different projects. So, for example, if you have a group of related repositories, you can develop a skin for one of them, then get a consistent look across all the repositories by exporting the skin from the first repository and importing into all the others. The file generated by "fossil config export" could be checked into one of your repositories and versioned, if desired. This will not automatically change the skin when looking backwards in time, but it will provide an historical record of what the skin used to be and allow the historical look of the repositories to be recreated if necessary. When cloning a repository, the skin of new repository is initialized to the skin of the repository from which it was cloned. Header And Footer Processing ---------------------------- The header.txt and footer.txt files of a scan are merely the HTML text of the header and footer. Except, before being prepended and appended to the content, the header and footer text are run through a [TH1 interpreter](./th1.md) that might adjust the text as follows: * All text within <th1>...</th1> is elided from the output and that text is instead run as a TH1 script. That TH1 script has the opportunity to insert new text in place of itself, or to inhibit or enable the output of subsequent text. * Text for the form "$NAME" or "$<NAME>" is replace with the value of the TH1 variable NAME. For example, the following is the first few lines of a typical header file: <html> <head> <base href="$baseurl/$current_page" /> <title>$<project_name>: $<title></title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" /> <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" /> </head> After variables are substituted by TH1, the final header text delivered to the web browser might look something like this: <html> <head> <base href="https://www.fossil-scm.org/skin2/timeline" /> <title>Fossil: Timeline</title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="/skin2/timeline.rss" /> <link rel="stylesheet" href="/skin2/style.css?default" type="text/css" media="screen" /> </head> The same TH1 interpreter is used for both the header and the footer and for all scripts contained within them both. Hence, any global TH1 variables that are set by the header are available to the footer. TH1 Variables ------------- Before expanding the TH1 within the header and footer, Fossil first initializes a number of TH1 variables to values that depend on respository settings and the specific page being generated. * **project_name** - The project_name variable is filled with the name of the project as configured under the Admin/Configuration menu. * **title** - The title variable holds the title of the page being generated. The title variable is special in that it is deleted after the header script runs and before the footer script. This is necessary to avoid a conflict with a variable by the same name used in my ticket-screen scripts. * **baseurl** - The root of the URL namespace for this server. * **secureurl** - The same as $baseurl except that if the scheme is "http:" it is changed to "https:" * **home** - The $baseurl without the scheme and hostname. For example, if the $baseurl is "http://projectX.com/cgi-bin/fossil" then the $home will be just "/cgi-bin/fossil". * **index_page** - The landing page URI as specified by the Admin/Configuration setup page. * **current_page** - The name of the page currently being processed, without the leading "/" and without query parameters. Examples: "timeline", "doc/trunk/README.txt", "wiki". * **csrf_token** - A token used to prevent cross-site request forgery. * **release_version** - The release version of Fossil. Ex: "1.31" * **manifest_version** - A prefix on the SHA1 check-in hash of the specific version of fossil that is running. Ex: "\[47bb6432a1\]" * **manifest_date** - The date of the source-code check-in for the version of fossil that is running. * **compiler_name** - The name and version of the compiler used to build the fossil executable. * **login** - This variable only exists if the user has logged in. The value is the username of the user. * **stylesheet_url** - A URL for the internal style-sheet maintained by Fossil. * **log\_image\_url** - A URL for the logo image for this project, as configured on the Admin/Logo page. * **background\_image\_url** - A URL for a background image for this project, as configured on the Admin/Logo page. All of the above are variables in the sense that either the header or the footer is free to change or erase them. But they should probably be treated as constants. New predefined values are likely to be added in future releases of Fossil. Suggested Skin Customization Procedure -------------------------------------- Developers are free, of course, to develop new skins using any method they want, but the following is a technique that has worked well in the past and can serve as a starting point for future work: 1. Select a built-in skin that is closest to the desired look. Make copies of the css, footer, and header into files name "css.txt", "details.txt", "footer.txt", and "header.txt" in some temporary directory. If the Fossil source code is available, then these three files can be copied directly out of one of the subdirectories under skins. If sources are not easily at hand, then a copy/paste out of the CSS, footer, and header editing screens under the Admin menu will work just as well. The important point is that the three files be named exactly "css.txt", "footer.txt", and "header.txt" and that they all be in the same directory. 2. Run the [fossil ui](../../../help?cmd=ui) command with an extra option "--skin SKINDIR" where SKINDIR is the name of the directory in which the three txt files were stored in step 1. This will bring up the Fossil website using the tree files in SKINDIR. 3. Edit the four txt files in SKINDIR. After making each small change, press Reload on the web browser to see the effect of that change. Iterate until the desired look is achieved. 4. Copy/paste the resulting css.txt, details.txt, header.txt, and footer.txt files into the CSS, details, header, and footer configuration screens under the Admin/Skins menu. |
Changes to www/delta_format.wiki.
︙ | ︙ | |||
64 65 66 67 68 69 70 | fully.</p> <a name="slist"></a><h3>1.3 Segment-List</h3> <img src="delta2.gif" align="left" hspace="10"> <p>The segment-list of a delta describes how to create the target from the original by a combination of inserting literal byte-sequences and | | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | fully.</p> <a name="slist"></a><h3>1.3 Segment-List</h3> <img src="delta2.gif" align="left" hspace="10"> <p>The segment-list of a delta describes how to create the target from the original by a combination of inserting literal byte-sequences and copying ranges of bytes from the original. This is where the compression takes place, by encoding the large common parts of original and target in small copy instructions.</p> <p>The target is constructed from beginning to end, with the data generated by each instruction appended after the data of all previous instructions, with no gaps.</p> |
︙ | ︙ | |||
153 154 155 156 157 158 159 | <tr><td> </td> <td>1B@Jd, </td><td>Copy </td><td> 75 @ 1256 </td></tr> <tr><td> </td> <td>6:scenda </td><td>Literal </td><td> 6 'scenda' </td></tr> <tr><td> </td> <td>5x@Kt, </td><td>Copy </td><td> 380 @ 1336 </td></tr> <tr><td> </td> <td>6:pieces </td><td>Literal </td><td> 6 'pieces' </td></tr> <tr><td> </td> <td>79@Qt, </td><td>Copy </td><td> 457 @ 1720 </td></tr> <tr><td> </td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr> <tr><td> </td> <td>~E@Y0, </td><td>Copy </td><td> 4046 @ 2176 </td></tr> | | | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | <tr><td> </td> <td>1B@Jd, </td><td>Copy </td><td> 75 @ 1256 </td></tr> <tr><td> </td> <td>6:scenda </td><td>Literal </td><td> 6 'scenda' </td></tr> <tr><td> </td> <td>5x@Kt, </td><td>Copy </td><td> 380 @ 1336 </td></tr> <tr><td> </td> <td>6:pieces </td><td>Literal </td><td> 6 'pieces' </td></tr> <tr><td> </td> <td>79@Qt, </td><td>Copy </td><td> 457 @ 1720 </td></tr> <tr><td> </td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr> <tr><td> </td> <td>~E@Y0, </td><td>Copy </td><td> 4046 @ 2176 </td></tr> <tr><td>Trailer</td><td>2zMM3E </td><td>Checksum</td><td> -1101438770 </td></tr> </table> <p>The unified diff behind the above delta is</p> <table border=1><tr><td><pre> bluepeak:(761) ~/Projects/Tcl/Fossil/Devel/devel > diff -u ../DELTA/old ../DELTA/new --- ../DELTA/old 2007-08-23 21:14:40.000000000 -0700 |
︙ | ︙ | |||
211 212 213 214 215 216 217 | <a name="notes"></a><h2>Notes</h2> <ul> <li>Pure text files generate a pure text delta. </li> <li>Binary files generate a delta that may contain some binary data. </li> | | | 211 212 213 214 215 216 217 218 219 220 221 222 223 | <a name="notes"></a><h2>Notes</h2> <ul> <li>Pure text files generate a pure text delta. </li> <li>Binary files generate a delta that may contain some binary data. </li> <li>The delta encoding does not attempt to compress the content. It was considered to be much more sensible to do compression using a separate general-purpose compression library, like <a href="http://www.zlib.net">zlib</a>. </li> </ul> |
Changes to www/embeddeddoc.wiki.
|
| | | | 1 2 3 4 5 6 7 8 9 | <title>Project Documentation</title> <h1 align="center">Project Documentation</h1> Fossil provides a built-in <a href="wikitheory.wiki">wiki</a> that can be used to store the documentation for a project. This is sufficient for many projects. If your project is well-served by wiki documentation, then you need read no further. |
︙ | ︙ | |||
61 62 63 64 65 66 67 | editing looks like before you check it in. Finally, the <i><filename></i> element of the URL is the pathname of the documentation file relative to the root of the source tree. The mimetype (and thus the rendering) of documentation files is | | > | | | < | > > > > > > > > > > > > > > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | editing looks like before you check it in. Finally, the <i><filename></i> element of the URL is the pathname of the documentation file relative to the root of the source tree. The mimetype (and thus the rendering) of documentation files is determined by the file suffix. Fossil currently understands [/mimetype_list|many different file suffixes], including all the popular ones such as ".css", ".gif", ".htm", ".html", ".jpg", ".jpeg", ".png", and ".txt". Documentation files whose names end in ".wiki" use the [/wiki_rules | same markup as wiki pages] - a safe subset of HTML together with some wiki rules for paragraph breaks, lists, and hyperlinks. Documentation files ending in ".md" or ".markdown" use the [/md_rules | Markdown markup langauge]. Documentation files ending in ".txt" are plain text. Wiki, markdown, and plain text documentation files are rendered with the standard fossil header and footer added. Most other mimetypes are delivered directly to the requesting web browser without interpretation, additions, or changes. Files with the mimetype "text/html" (the .html or .htm suffix) are usually rendered directly to the browser without interpretation. However, if the file begins with a <div> element like this: <b><div class='fossil-doc' data-title='<i>Title Text</i>'></b> Then the standard Fossil header and footer are added to the document prior to being displayed. The "class='fossil-doc'" attribute is required for this to occur. The "data-title='...'" attribute is optional, but if it is present the text will become the title displayed in the Fossil header. An example of this can be seen in the text of the [/artifact/84b4b3d041d93a?txt=1 | Index Of Fossil Documentation] document. <h2>Examples</h2> This file that you are currently reading is an example of embedded documentation. The name of this file in the fossil source tree is "<b>www/embeddeddoc.wiki</b>". You are perhaps looking at this |
︙ | ︙ |
Changes to www/event.wiki.
|
| | | > | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | <title>Technical Notes</title> <h2>What Is A "Technote"?</h2> In Fossil, a "technical note" or "technote" (formerly called an "event") is a special kind of [./wikitheory.wiki | wiki page] that is associated with a point in time rather than having a page name. Each technote causes a single entry to appear on the [/timeline?y=e | Timeline Page]. Clicking on the timeline link will display the text of the technote. The wiki content, the timeline entry text, the time of the technote, and the timeline background color can all be edited. As with check-ins, wiki, and tickets, all technotes automatically synchronize to other repositories. Hence, technotes can be viewed, created, and edited off-line. And the complete edit history for technotes is maintained for auditing purposes. Possible uses for technotes include: * <b>Milestones</b>. Project milestones, such as releases or beta-test cycles, can be recorded as technotes. The timeline entry for the technote can be something simple like "Version 1.2.3" perhaps with a bright color background to draw attention to the entry and the wiki content can contain release notes, for example. * <b>Blog Entries</b>. Blog entries from developers describing the current state of a project, or rational for various design decisions, or roadmaps for future development, can be entered as technotes. * <b>Process Checkpoints</b>. For projects that have a formal process, technotes can be used to record the completion or the initiation of various process steps. For example, a technote can be used to record the successful completion of a long-running test, perhaps with performance results and details of where the test was run and who ran it recorded in the wiki content. * <b>News Articles</b>. Significant occurrences in the lifecycle of a project can be recorded as news articles using technotes. Perhaps the domain name of the canonical website for a project changes, or new server hardware is obtained. Such happenings are appropriate for reporting as news. * <b>Announcements</b>. Changes to the composition of the development team or acquisition of new project sponsors can be communicated as announcements which can be implemented as technotes. No project is required to use technotes. But technotes can help many projects stay better organized and provide a better historical record of the development progress. <h2>Viewing Technotes</h2> Because technotes are considered a special kind of wiki, users must have permission to read wiki in order read technotes. Enable the "j" permission under the /Setup/Users menu in order to give specific users or user classes the ability to view wiki and technotes. Technotes show up on the timeline. Click on the hyperlink beside the technote title to see the complete text. <h2>Creating And Editing Technotes</h2> There is a hyperlink under the /wikihelp menu that can be used to create new technotes. And there is a submenu hyperlink on technote displays for editing existing technotes. Users must have check-in privileges (permission "i") in order to create or edit technotes. In addition, users must have create-wiki privilege (permission "f") to create new technotes and edit-wiki privilege (permission "k") in order to edit existing technotes. Technote content may be formatted as [/wiki_rules | Fossil wiki], [/md_rules | Markdown], or a plain text. |
Changes to www/faq.tcl.
︙ | ︙ | |||
53 54 55 56 57 58 59 | off from. If you already have a fork in your check-in tree and you want to convert that fork to a branch, you can do this from the web interface. First locate the check-in that you want to be the initial check-in of your branch on the timeline and click on its link so that you are on the <b>ci</b> page. Then find the "<b>edit</b>" | | | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | off from. If you already have a fork in your check-in tree and you want to convert that fork to a branch, you can do this from the web interface. First locate the check-in that you want to be the initial check-in of your branch on the timeline and click on its link so that you are on the <b>ci</b> page. Then find the "<b>edit</b>" link (near the "Commands:" label) and click on that. On the "Edit Check-in" page, check the box beside "Branching:" and fill in the name of your new branch to the right and press the "Apply Changes" button. } faq { How do I tag a check-in? } { |
︙ | ︙ | |||
81 82 83 84 85 86 87 | <b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i> </blockquote> The CHECK-IN in the previous line can be any [./checkin_names.wiki | valid check-in name format]. You can also add (and remove) tags from a check-in using the | | | | | | | | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | <b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i> </blockquote> The CHECK-IN in the previous line can be any [./checkin_names.wiki | valid check-in name format]. You can also add (and remove) tags from a check-in using the [./webui.wiki | web interface]. First locate the check-in that you what to tag on the timeline, then click on the link to go the detailed information page for that check-in. Then find the "<b>edit</b>" link (near the "Commands:" label) and click on that. There are controls on the edit page that allow new tags to be added and existing tags to be removed. } faq { How do I create a private branch that won't get pushed back to the main repository. } { Use the <b>--private</b> command-line option on the <b>commit</b> command. The result will be a check-in which exists on your local repository only and is never pushed to other repositories. All descendants of a private check-in are also private. Unless you specify something different using the <b>--branch</b> and/or <b>--bgcolor</b> options, the new private check-in will be put on a branch named "private" with an orange background color. You can merge from the trunk into your private branch in order to keep your private branch in sync with the latest changes on the trunk. Once you have everything in your private branch the way you want it, you can then merge your private branch back into the trunk and push. Only the final merge operation will appear in other repositories. It will seem as if all the changes that occurred on your private branch occurred in a single check-in. |
︙ | ︙ |
Changes to www/faq.wiki.
︙ | ︙ | |||
85 86 87 88 89 90 91 | </blockquote> The CHECK-IN in the previous line can be any [./checkin_names.wiki | valid check-in name format]. You can also add (and remove) tags from a check-in using the [./webui.wiki | web interface]. First locate the check-in that you | | | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | </blockquote> The CHECK-IN in the previous line can be any [./checkin_names.wiki | valid check-in name format]. You can also add (and remove) tags from a check-in using the [./webui.wiki | web interface]. First locate the check-in that you what to tag on the timeline, then click on the link to go the detailed information page for that check-in. Then find the "<b>edit</b>" link (near the "Commands:" label) and click on that. There are controls on the edit page that allow new tags to be added and existing tags to be removed.</blockquote></li> <a name="q5"></a> <p><b>(5) How do I create a private branch that won't get pushed back to the main repository.</b></p> <blockquote>Use the <b>--private</b> command-line option on the <b>commit</b> command. The result will be a check-in which exists on your local repository only and is never pushed to other repositories. All descendants of a private check-in are also private. Unless you specify something different using the <b>--branch</b> and/or <b>--bgcolor</b> options, the new private check-in will be put on a branch named "private" with an orange background color. You can merge from the trunk into your private branch in order to keep your private branch in sync with the latest changes on the trunk. Once |
︙ | ︙ |
Changes to www/fileformat.wiki.
︙ | ︙ | |||
26 27 28 29 30 31 32 | The local state is not versioned and is not synchronized with the global state. The local state is not composed of artifacts and is not intended to be enduring. This document is concerned with global state only. Local state is only mentioned here in order to distinguish it from global state. Each artifact in the repository is named by its SHA1 hash. | | | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | The local state is not versioned and is not synchronized with the global state. The local state is not composed of artifacts and is not intended to be enduring. This document is concerned with global state only. Local state is only mentioned here in order to distinguish it from global state. Each artifact in the repository is named by its SHA1 hash. No prefixes or meta information is added to an artifact before its hash is computed. The name of an artifact in the repository is exactly the same SHA1 hash that is computed by sha1sum on the file as it exists in your source tree.</p> Some artifacts have a particular format which gives them special meaning to fossil. Fossil recognizes: <ul> <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#attachment | Attachments] </li> <li> [#event | TechNotes] </li> </ul> These seven artifact types are described in the following sections. In the current implementation (as of 2009-01-25) the artifacts that make up a fossil repository are stored as delta- and zlib-compressed blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This is an implementation detail and might change in a future release. For the purpose of this article "file format" means the format of the artifacts, not how the artifacts are stored on disk. It is the artifact format that is intended to be enduring. The specifics of how artifacts are stored on disk, though stable, is not intended to live as long as the artifact format. |
︙ | ︙ | |||
111 112 113 114 115 116 117 | </blockquote> A manifest may optionally have a single B-card. The B-card specifies another manifest that serves as the "baseline" for this manifest. A manifest that has a B-card is called a delta-manifest and a manifest that omits the B-card is a baseline-manifest. The other manifest identified by the argument of the B-card must be a baseline-manifest. | | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | </blockquote> A manifest may optionally have a single B-card. The B-card specifies another manifest that serves as the "baseline" for this manifest. A manifest that has a B-card is called a delta-manifest and a manifest that omits the B-card is a baseline-manifest. The other manifest identified by the argument of the B-card must be a baseline-manifest. A baseline-manifest records the complete contents of a check-in. A delta-manifest records only changes from its baseline. A manifest must have exactly one C-card. The sole argument to the C-card is a check-in comment that describes the check-in that the manifest defines. The check-in comment is text. The following escape sequences are applied to the text: A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A |
︙ | ︙ | |||
136 137 138 139 140 141 142 | <blockquote> <i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i><br> <i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i><b>.</b><i>SSS</i> </blockquote> A manifest has zero or more F-cards. Each F-card identifies a file | | < | | | | | | | | | | > | | | > | | < | | | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | <blockquote> <i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i><br> <i>YYYY</i><b>-</b><i>MM</i><b>-</b><i>DD</i><b>T</b><i>HH</i><b>:</b><i>MM</i><b>:</b><i>SS</i><b>.</b><i>SSS</i> </blockquote> A manifest has zero or more F-cards. Each F-card identifies a file that is part of the check-in. There are one, two, three, or four arguments. The first argument is the pathname of the file in the check-in relative to the root of the project file hierarchy. No ".." or "." directories are allowed within the filename. Space characters are escaped as in C-card comment text. Backslash characters and newlines are not allowed within filenames. The directory separator character is a forward slash (ASCII 0x2F). The second argument to the F-card is the full 40-character lower-case hexadecimal SHA1 hash of the content artifact. The second argument is required for baseline manifests but is optional for delta manifests. When the second argument to the F-card is omitted, it means that the file has been deleted relative to the baseline (files removed in baseline manifests versions are <em>not</em> added as F-cards). The optional 3rd argument defines any special access permissions associated with the file. This can be defined as "x" to mean that the file is executable or "l" (small letter ell) to mean a symlink. All files are always readable and writable. This can be expressed by "w" permission if desired but is optional. The file format might be extended with new permission letters in the future. The optional 4th argument is the name of the same file as it existed in the parent check-in. If the name of the file is unchanged from its parent, then the 4th argument is omitted. A manifest has zero or one N-cards. The N-card specifies the mimetype for the text in the comment of the C-card. If the N-card is omitted, a default mimetype is used. A manifest has zero or one P-cards. Most manifests have one P-card. The P-card has a varying number of arguments that |
︙ | ︙ | |||
182 183 184 185 186 187 188 | in that it defines a predecessor to the current check-in. But whereas a P-card defines the immediate ancestor or a merge ancestor, the Q-card is used to identify a single check-in or a small range of check-ins which were cherry-picked for inclusion in or exclusion from the current manifest. The first argument of the Q-card is the artifact ID of another manifest (the "target") which has had its changes included or excluded in the current manifest. | | | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | in that it defines a predecessor to the current check-in. But whereas a P-card defines the immediate ancestor or a merge ancestor, the Q-card is used to identify a single check-in or a small range of check-ins which were cherry-picked for inclusion in or exclusion from the current manifest. The first argument of the Q-card is the artifact ID of another manifest (the "target") which has had its changes included or excluded in the current manifest. The target is preceded by "+" or "-" to show inclusion or exclusion, respectively. The optional second argument to the Q-card is another manifest artifact ID which is the "baseline" for the cherry-pick. If omitted, the baseline is the primary parent of the target. The changes included or excluded consist of all changes moving from the baseline to the target. |
︙ | ︙ | |||
236 237 238 239 240 241 242 | A sample manifest from Fossil itself can be seen [/artifact/28987096ac | here]. <a name="cluster"></a> <h2>2.0 Clusters</h2> | | | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | A sample manifest from Fossil itself can be seen [/artifact/28987096ac | here]. <a name="cluster"></a> <h2>2.0 Clusters</h2> A cluster is an artifact that declares the existence of other artifacts. Clusters are used during repository synchronization to help reduce network traffic. As such, clusters are an optimization and may be removed from a repository without loss or damage to the underlying project code. Clusters follow a syntax that is very similar to manifests. A Cluster is a line-oriented text file. Newline characters |
︙ | ︙ | |||
311 312 313 314 315 316 317 | some other artifact. The T card has two or three values. The second argument is the 40 character lowercase artifact ID of the artifact to which the tag is to be applied. The first value is the tag name. The first character of the tag is either "+", "-", or "*". The "+" means the tag should be added to the artifact. The "-" means the tag should be removed. The "*" character means the tag should be added to the artifact | | | | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | some other artifact. The T card has two or three values. The second argument is the 40 character lowercase artifact ID of the artifact to which the tag is to be applied. The first value is the tag name. The first character of the tag is either "+", "-", or "*". The "+" means the tag should be added to the artifact. The "-" means the tag should be removed. The "*" character means the tag should be added to the artifact and all direct descendants (but not descendants through a merge) down to but not including the first descendant that contains a more recent "-", "*", or "+" tag with the same name. The optional third argument is the value of the tag. A tag without a value is a Boolean. When two or more tags with the same name are applied to the same artifact, the tag with the latest (most recent) date is used. Some tags have special meaning. The "comment" tag when applied to a check-in will override the check-in comment of that check-in |
︙ | ︙ | |||
357 358 359 360 361 362 363 | <b>Z</b> <i>checksum</i> </blockquote> The D card is the date and time when the wiki page was edited. The P card specifies the parent wiki pages, if any. The L card gives the name of the wiki page. The optional N card specifies the mimetype of the wiki text. If the N card is omitted, the | | | | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | <b>Z</b> <i>checksum</i> </blockquote> The D card is the date and time when the wiki page was edited. The P card specifies the parent wiki pages, if any. The L card gives the name of the wiki page. The optional N card specifies the mimetype of the wiki text. If the N card is omitted, the mimetype is assumed to be text/x-fossil-wiki. The U card specifies the login of the user who made this edit to the wiki page. The Z card is the usual checksum over the entire artifact and is required. The W card is used to specify the text of the wiki page. The argument to the W card is an integer which is the number of bytes of text in the wiki page. That text follows the newline character that terminates the W card. The wiki text is always followed by one extra newline. |
︙ | ︙ | |||
421 422 423 424 425 426 427 | An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <a name="attachment"></a> <h2>6.0 Attachments</h2> An attachment artifact associates some other artifact that is the | | > | | | | > | | | | | | | | | | | | | | | | | | | | | | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 | An example ticket-change artifact can be seen [/artifact/91f1ec6af053 | here]. <a name="attachment"></a> <h2>6.0 Attachments</h2> An attachment artifact associates some other artifact that is the attachment (the source artifact) with a ticket or wiki page or technical note to which the attachment is connected (the target artifact). The following cards are allowed on an attachment artifact: <blockquote> <b>A</b> <i>filename target</i> ?<i>source</i>?<br /> <b>C</b> <i>comment</i><br /> <b>D</b> <i>time-and-date-stamp</i><br /> <b>N</b> <i>mimetype</i><br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i> </blockquote> The A card specifies a filename for the attachment in its first argument. The second argument to the A card is the name of the wiki page or ticket or technical note to which the attachment is connected. The third argument is either missing or else it is the 40-character artifact ID of the attachment itself. A missing third argument means that the attachment should be deleted. The C card is an optional comment describing what the attachment is about. The C card is optional, but there can only be one. A single D card is required to give the date and time when the attachment was applied. There may be zero or one N cards. The N card specifies the mimetype of the comment text provided in the C card. If the N card is omitted, the C card mimetype is taken to be text/plain. A single U card gives the name of the user who added the attachment. If an attachment is added anonymously, then the U card may be omitted. The Z card is the usual checksum over the rest of the attachment artifact. The Z card is required. <a name="event"></a> <h2>7.0 Technical Notes</h2> A technical note or "technote" artifact (formerly known as an "event" artifact) associates a timeline comment and a page of text (similar to a wiki page) with a point in time. Technotes can be used to record project milestones, release notes, blog entries, process checkpoints, or news articles. The following cards are allowed on an technote artifact: <blockquote> <b>C</b> <i>comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br /> <b>E</b> <i>technote-time</i> <i>technote-id</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</i>+<br /> <b>T</b> <b>+</b><i>tag-name</i> <b>*</b> ?<i>value</i>?<br /> <b>U</b> <i>user-name</i><br /> <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> <b>Z</b> <i>checksum</i> </blockquote> The C card contains text that is displayed on the timeline for the technote. The C card is optional, but there can only be one. A single D card is required to give the date and time when the technote artifact was created. This is different from the time at which the technote appears on the timeline. A single E card gives the time of the technote (the point on the timeline where the technote is displayed) and a unique identifier for the technote. When there are multiple artifacts with the same technote-id, the one with the most recent D card is the only one used. The technote-id must be a 40-character lower-case hexadecimal string. The optional N card specifies the mimetype of the text of the technote that is contained in the W card. If the N card is omitted, then the W card text mimetype is assumed to be text/x-fossil, which is the Fossil wiki format. The optional P card specifies a prior technote with the same technote-id from which the current technote is an edit. The P card is a hint to the system that it might be space efficient to store one technote as a delta of the other. A technote might contain one or more T-cards used to set [./branching.wiki#tags | tags or properties] on the technote. The format of the T-card is the same as described in [#ctrl | Control Artifacts] section above, except that the second argument is the single character "<b>*</b>" instead of an artifact ID and the name is always prefaced by "<b>+</b>". The <b>*</b> in place of the artifact ID indicates that the tag or property applies to the current artifact. It is not possible to encode the current artifact ID as part of an artifact, since the act of inserting the artifact ID would change the artifact ID, hence a <b>*</b> is used to represent "self". The "<b>+</b>" on the name means that tags can only be add and they can only be non-propagating tags. In a technote, T cards are normally used to set the background display color for timelines. The optional U card gives name of the user who entered the technote. A single W card provides wiki text for the document associated with the technote. The format of the W card is exactly the same as for a [#wikichng | wiki artifact]. The Z card is the required checksum over the rest of the artifact. <a name="summary"></a> <h2>8.0 Card Summary</h2> |
︙ | ︙ | |||
548 549 550 551 552 553 554 | <tr> <th>Manifest</th> <th>Cluster</th> <th>Control</th> <th>Wiki</th> <th>Ticket</th> <th>Attachment</th> | | | 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 | <tr> <th>Manifest</th> <th>Cluster</th> <th>Control</th> <th>Wiki</th> <th>Ticket</th> <th>Attachment</th> <th>Technote</th> </tr> <tr> <td><b>A</b> <i>filename</i> <i>target</i> ?<i>source</i>?</td> <td> </td> <td> </td> <td> </td> <td> </td> |
︙ | ︙ | |||
592 593 594 595 596 597 598 | <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> </tr> <tr> | | | 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 | <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> <td align=center><b>1</b></td> </tr> <tr> <td><b>E</b> <i>technote-time technote-id</i></td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td> </td> <td align=center><b>1</b></td> |
︙ | ︙ |
Changes to www/fiveminutes.wiki.
1 2 3 4 5 6 7 8 9 | <title>Up and running in 5 minutes as a single user</title> <p align="center"><b><i> The following document was contributed by Gilles Ganault on 2013-01-08. </i></b> </p><hr> <h1>Up and running in 5 minutes as a single user</h1> <p>This short document explains the main basic Fossil commands for a single | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | <title>Up and running in 5 minutes as a single user</title> <p align="center"><b><i> The following document was contributed by Gilles Ganault on 2013-01-08. </i></b> </p><hr> <h1>Up and running in 5 minutes as a single user</h1> <p>This short document explains the main basic Fossil commands for a single user, i.e. with no additional users, with no need to synchronize with some remote repository, and no need for branching/forking.</p> <h2>Create a new repository</h2> <p>fossil new c:\test.repo</p> <p>This will create the new SQLite binary file that holds the repository, i.e. files, tickets, wiki, etc. It can be located anywhere, although it's considered best practice to keep it outside the work directory where you will work on files after they've been checked out of the repository.</p> <h2>Open the repository</h2> <p>cd c:\temp\test.fossil</p> <p>fossil open c:\test.repo</p> <p>This will check out the last revision of all the files in the repository, if any, into the current work directory. In addition, it will create a binary file _FOSSIL_ to keep track of changes (on non-Windows systems it is called <tt>.fslckout</tt>).</p> <h2>Add new files</h2> <p>fossil add .</p> <p>To tell Fossil to add new files to the repository. The files aren't actually added until you run "commit". When using ".", it tells Fossil to add all the files in the current directory recursively, i.e. including all the files in all the subdirectories.</p> <p>Note: To tell Fossil to ignore some extensions:</p> <p>fossil settings ignore-glob "*.o,*.obj,*.exe" --global</p> <h2>Remove files that haven't been committed yet</h2> <p>fossil delete myfile.c</p> <p>This will simply remove the item from the list of files that were previously added through "fossil add".</p> <h2>Check current status</h2> <p>fossil changes</p> <p>This shows the list of changes that have been done and will be committed the next time you run "fossil commit". It's a useful command to run before running "fossil commit" just to check that things are OK before proceeding.</p> <h2>Commit changes</h2> <p>To actually apply the pending changes to the repository, e.g. new files marked for addition, checked-out files that have been edited and must be checked-in, etc.</p> <p>fossil commit -m "Added stuff"</p> If no file names are provided on the command-line then all changes will be checked in, otherwise just the listed file(s) will be checked in. <h2>Compare two revisions of a file</h2> <p>If you wish to compare the last revision of a file and its checked out version in your work directory:</p> <p>fossil gdiff myfile.c</p> <p>If you wish to compare two different revisions of a file in the repository:</p> <p>fossil finfo myfile: Note the first hash, which is the UUID of the commit when the file was committed</p> <p>fossil gdiff --from UUID#1 --to UUID#2 myfile.c</p> <h2>Cancel changes and go back to previous revision</h2> <p>fossil revert myfile.c</p> <p>Fossil does not prompt when reverting a file. It simply reminds the user about the "undo" command, just in case the revert was a mistake.</p> <h2>Close the repository</h2> <p>fossil close</p> <p>This will simply remove the _FOSSIL_ at the root of the work directory but will not delete the files in the work directory. From then on, any use of "fossil" will trigger an error since there is no longer any connection.</p> |
Changes to www/foss-cklist.wiki.
1 2 3 4 5 6 7 8 9 | <title>Checklist For Successful Open-Source Projects</title> <nowiki> <p>This checklist is loosely derived from Tom "Spot" Callaway's Fail Score blog post <a href="http://spot.livejournal.com/308370.html"> http://spot.livejournal.com/308370.html</a> (see also <a href="http://www.theopensourceway.org/book/The_Open_Source_Way-How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL.html">[1]</a> and <a href="https://www.theopensourceway.org/wiki/How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL">[2]</a>). Tom's original post assigned point scores to the various elements and | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <title>Checklist For Successful Open-Source Projects</title> <nowiki> <p>This checklist is loosely derived from Tom "Spot" Callaway's Fail Score blog post <a href="http://spot.livejournal.com/308370.html"> http://spot.livejournal.com/308370.html</a> (see also <a href="http://www.theopensourceway.org/book/The_Open_Source_Way-How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL.html">[1]</a> and <a href="https://www.theopensourceway.org/wiki/How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL">[2]</a>). Tom's original post assigned point scores to the various elements and by adding together the individual points, the reader is supposed to be able to judge the likelihood that the project will fail. The point scores, and the items on the list, clearly reflect Tom's biases and are not necessarily those of the larger open-source community. Nevertheless, the policy of the Fossil shall be to strive for a perfect score.</p> <p>This checklist is an inversion of Tom's original post in that it strives to |
︙ | ︙ |
Changes to www/fossil-v-git.wiki.
1 2 3 4 5 6 7 | <title>Fossil Versus Git</title> <h2>1.0 Don't Stress!</h2> If you start out using one DVCS and later decide you like the other better, it is [./inout.wiki | easy to change]. | | | | | > > > | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | <title>Fossil Versus Git</title> <h2>1.0 Don't Stress!</h2> If you start out using one DVCS and later decide you like the other better, it is [./inout.wiki | easy to change]. But it also helps to be informed about the differences between [http://git-scm.com | Git] and Fossil. See the table below for a high-level summary and the text that follows for more details. Keep in mind that you are reading this on a Fossil website, so the information here might be biased in favor of Fossil. Ask around with people who have used both Fossil and Git for other opinions. <h2>2.0 Executive Summary:</h2> <blockquote><center><table border=1 cellpadding=5> <tr><th width="50%">GIT</th><th width="50%">FOSSIL</th></tr> <tr><td>File versioning only</td> <td>Versioning, Tickets, Wiki, and Technotes</td></tr> <tr><td>Sharding</td><td>Replicating</td></tr> <tr><td>Developer branches</td><td>Feature branches</td></tr> <tr><td>Complex</td><td>Intuitive</td></tr> <tr><td>Separate web tools</td><td>Integrated Web interface</td></tr> <tr><td>Lots of little tools</td><td>Single executable</td></tr> <tr><td>Pile-of-files repository</td> <td>Single-file relational database</td></tr> <tr><td>One check-out per repository</td> <td>Many check-outs per repository</td></tr> <tr><td>Uses "<tt>rebase</tt>"</td><td>Immutable</td></tr> <tr><td>GPL</td><td>BSD</td></tr> </table></center></blockquote> <h2>3.0 Discussion</h2> <h3>3.1 Feature Set</h3> Git provides file versioning services only, whereas Fossil adds an integrated [./wikitheory.wiki | wiki], [./bugtheory.wiki | ticketing & bug tracking], [./embeddeddoc.wiki | embedded documentation], and [./event.wiki | Technical notes]. These additional capabilities are available for Git as 3rd-party user-installed add-ons, but with Fossil they are integrated into the design. One way to describe Fossil is that it is "[https://github.com/ | github]-in-a-box". <h3>3.2 Sharding versus Replicating</h3> Git makes it easy for each repository in a project to hold a subset of the branches for that project. In fact, it is entirely possible and not uncommon for no repository in the project to hold all the different code versions for a project. Instead the information is distributed. Individual developers have one or more private branches. A hierarchy of integrators merge changes from individual developers into collaborative branches, until all the changes are merged together at the top-level master branch. And all of this can be accomplished without having to have all the code in any one repository. Developers or groups of developers can share only those branches that they want to share and keep other branches of the project private. This is analogous to sharding a distributed database. Fossil allows private branches, but its default mode is to share everything. And so in a Fossil project, all repositories tend to contain all of the content at all times. This is analogous to replication in a distributed database. The Git model works best for large projects, like the Linux kernel for which Git was designed. Linus Torvalds does not need or want to see a thousand different branches, one for each contributor. Git allows intermediary "gate-keepers" to merge changes from multiple lower-level developers into a single branch and only present Linus with a handful of branches at a time. Git encourages a programming model where each developer works in his or her own branch and then merges changes up the hierarchy until they reach the master branch. Fossil is designed for smaller and non-hierarchical teams where all developers are operating directly on the master branch, or at most a small number of well-defined branches. The [./concepts.wiki#workflow | autosync] mode of Fossil makes it easy for multiple developers to work on a single branch and maintain linear development on that branch and avoid needless forking and merging. <h3>3.3 Branches</h3> Git (and especially GitHub) encourages a workflow where each developer has his or her own branch or branches. Developers then send "pull requests" to have their changes be merged into "official" branches by integrators. For example, the Linux kernel team has a hierarchy of integrators with Linus Torvalds at the root. Individual developers each have their own private branches of the source tree into which they make their own changes. They then encourage first-tier integrators to pull those changes. The first-tier integrators merge together changes from multiple contributors then try to get second-tier integrators to pull their branches. The changes merge up the hierarchy until (hopefully) they are pulled into "Linus's branch", at which time they become part of the "official" Linux. In Git, each branch is "owned" by the person who creates it and works on it. The owner might pull changes from others, but the owner is always in control of the branch. Branches are developer-centric. Fossil, on the other hand, encourages a workflow where branches are associated with features or releases, not individual developers. All developers share all branches in common, and two or more developers can and often do intersperse commits onto the same branch. Branches do not belong to individuals. All branches are read/write accessible to all developers at all times. There is no need for integrators to merge together changes from various independent developers. Instead, all of the developers work together cooperatively and the changes stay integrated naturally. So to a first approximation, branches in Git are developer-centric whereas branches in Fossil are feature-centric. The Git approach scales much better for large projects like the Linux kernel with thousands of contributors who in many cases don't even know each other's names. The integrators serve a gatekeeper role to help keep undesirable code out of the official Linux source tree. On the other hand, not many projects are as big or as loosely organized as the Linux kernel. Most projects have a small team of developers who all know each other well and trust each other, and who enjoy working together collaboratively without the overhead and hierarchy of integrators. One consequence of the "everybody-sees-everything" focus of Fossil is that branch names are global and are part of the distributed and synchronized |
︙ | ︙ | |||
139 140 141 142 143 144 145 | development. One should not have to stop and think about how to operate the VCS. Of course, no VCS is ideal. Every VCS requires the developer to think about version control to some extent. But one wants to minimize the thinking about version control. Git requires the developer to maintain a more complex mental model than most other DVCSes. Git takes longer to learn. And you have to spend | | | > > > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | development. One should not have to stop and think about how to operate the VCS. Of course, no VCS is ideal. Every VCS requires the developer to think about version control to some extent. But one wants to minimize the thinking about version control. Git requires the developer to maintain a more complex mental model than most other DVCSes. Git takes longer to learn. And you have to spend more time thinking about what you are doing with Git. Fossil strives for simplicity. Fossil wants to be easy to learn and to require little thinking about how to operating it. [./quotes.wiki | Reports from the field] indicate that Fossil is mostly successful at this effort. Fossil will <u>never</u> get you into anything like the "disconnected head state" which has frustrated so many Git users. <h3>3.5 Web Interface</h3> Git has a web interface, but it requires a fair amount of setup and an external web server. Fossil comes with a fully functional [./webui.wiki | built-in web-server] and a really simple mechanism (the "[/help/ui|fossil ui]" command) to automatically start the web server and bring up a web browser to navigate |
︙ | ︙ | |||
183 184 185 186 187 188 189 | A Git repository is a "pile-of-files" in the ".git" directory at the root of the working checkout. There is a one-to-one correspondence between repositories and working checkouts. A power-loss or system crash in the middle of Git operation can damage or corrupt the Git repository. A Fossil repository consists of a single disk file. A single Fossil repository can serve multiple simultaneous working checkouts. | | > > > > > > > > > > > > > > > > | | | | | | | | | | > > > > | > > > > > > > > > | | > > > > > > > > > > | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | A Git repository is a "pile-of-files" in the ".git" directory at the root of the working checkout. There is a one-to-one correspondence between repositories and working checkouts. A power-loss or system crash in the middle of Git operation can damage or corrupt the Git repository. A Fossil repository consists of a single disk file. A single Fossil repository can serve multiple simultaneous working checkouts. A Fossil repository is an SQLite database, so it is highly resistant to damage from a power-loss or system crash - incomplete transactions are simply rolled back after the system reboots. <h3>3.8 Check-outs Per Repository</h3> In Git, a check-out and a repository are joined in a fundamental way so that only a single version of the project history, or a single branch, can be open at once. If you have a project with multiple branches and you want to have two or more branches open at the same time (perhaps to do performance comparisons, or maybe to run simultaneous builds using different compile-time options) then in Git you actually have to create a new clone of the repository for each open checkout. In Fossil, the repository and the check-out are distinct entities and so a single repository can support multiple simultaneous checkouts. This feature is <em>extensively</em> used by the Fossil developers themselves. Perhaps we are biased, but we not understand how anyone can work efficiently with just one check-out per repository. <h3>3.9 Audit Trail</h3> Git features the "rebase" command which can be used to change the sequence of check-ins in the repository. Rebase can be used to "clean up" a complex sequence of check-ins to make their intent easier for others to understand. This is important if you view the history of a project as part of the documentation for the project. Fossil takes an opposing view. Fossil views history as sacrosanct and stubbornly refuses to change it. Fossil allows mistakes to be corrected (for example, check-in comments can be revised, and check-ins can be moved onto new branches even after the check-in has occurred) but the correction is an addition to the repository and the original actions are preserved and displayed alongside the corrections, thus preserving an historically accurate audit trail. This is analogous to an accounting practice of marking through an incorrect entry in a ledger and writing a correction beside it. To put it another way, Git remembers what you should have done whereas Fossil remembers what you actually did. The lack of a "rebase" command and the inability to rewrite history is considered a feature of Fossil, not an omission or bug. <h3>3.10 License</h3> Both Git and Fossil are open-source. Git is under [http://www.gnu.org/licenses/gpl.html | GPL] whereas Fossil is under the [http://en.wikipedia.org/wiki/BSD_licenses | two-clause BSD license]. The different licenses parallel, to some extent, the different philosophies of Git and Fossil. There are exceptions on both sides, but to a first approximation, Git works better for GPL projects and Fossil works better for BSD projects. The GPL is designed to provide a very contributor-friendly environment. No legal paperwork is needed to contribute to a GPL project because the GPL is cleverly designed so that the act of contributing to the project (or even reading the code for the project) constitutes an acceptance of the licensing terms. GPL encourages a bazaar-style development model, with lots of anonymous programmers contributing drive-by patches. The theory is that with many eyeballs, all bugs are shallow. Surprisingly, this has actually been demonstrated to work in many well-known projects. The BSD-style licenses are more user-friendly. BSD-style licenses place fewer restrictions on the users of the software at the expense of making it more difficult to contribute changes or enhancements. To protect against IP claims, every contributor to a BSD-style project must sign legal documents in which they agree to release their contributions under the same license. (Some BSD-licensed projects omit this formality, but do so at their peril.) A BSD-style license encourages a more cathedral-style approach to development. There is a small team of developers. Drive-by patches and anonymous contributors are discouraged and/or prohibited. Contributors are expected to be experts and be available to support their changes for the long-term. |
Changes to www/hacker-howto.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <title>Fossil Hackers How-To</title> The following links are of interest to programmers who want to modify or enhance Fossil. Ordinary users can safely ignore this information. * [./build.wiki | How To Compile And Install Fossil] * [./makefile.wiki | The Fossil Build Process] * [./tech_overview.wiki | A Technical Overview of Fossil] * [./adding_code.wiki | Adding Features To Fossil] * [./contribute.wiki|Contributing Code Or Enhancements To The Fossil Project] * [./style.wiki | Coding Style Guidelines] * [./checkin.wiki | Pre-checkin Checklist] * [../test/release-checklist.wiki | Release Checklist] | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <title>Fossil Hackers How-To</title> The following links are of interest to programmers who want to modify or enhance Fossil. Ordinary users can safely ignore this information. * [./build.wiki | How To Compile And Install Fossil] * [./customskin.md | Theming Fossil] * [./makefile.wiki | The Fossil Build Process] * [./tech_overview.wiki | A Technical Overview of Fossil] * [./adding_code.wiki | Adding Features To Fossil] * [./contribute.wiki|Contributing Code Or Enhancements To The Fossil Project] * [./style.wiki | Coding Style Guidelines] * [./checkin.wiki | Pre-checkin Checklist] * [../test/release-checklist.wiki | Release Checklist] |
Changes to www/hints.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <title>Fossil Tips And Usage Hints</title> 1. Click on nodes of any timeline graph to see diffs between the two selected versions. 2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands to get a pop-up window containing a complete side-by-side diff. (NB: The pop-up window is run as a separate Tcl/Tk process, so you will need to have Tcl/Tk installed on your machine for this to work. Visit [http://www.activestate.com/activetcl] to for a quick download of Tcl/Tk if you do not already have it on your system.) | | > < < | < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <title>Fossil Tips And Usage Hints</title> 1. Click on nodes of any timeline graph to see diffs between the two selected versions. 2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands to get a pop-up window containing a complete side-by-side diff. (NB: The pop-up window is run as a separate Tcl/Tk process, so you will need to have Tcl/Tk installed on your machine for this to work. Visit [http://www.activestate.com/activetcl] to for a quick download of Tcl/Tk if you do not already have it on your system.) 3. The "[/help/clean | fossil clean -f]" or "[/help/clean | fossil clean --verily]" command is a great alternative to "make clean". 4. Use "[/help?cmd=all | fossil all changes]" to look for any uncommitted edits in any of your Fossil projects. Use "[/help?cmd=all | fossil all pull]" on your laptop prior to going off network (for example, on a long plane ride) to make sure you have all the latest content locally. Then run "[/help/all|fossil all push]" when you get back online to upload your changes. 5. To see an entire timeline, type "all" into the "Max:" entry box. 6. You can manually add a "c=CHECKIN" query parameter to the timeline URL to get a snapshot of what was going on about the time of some check-in. The "CHECKIN" can be [./checkin_names.wiki | any valid check-in or version name], including tags, branch names, and dates. For example, to see what was going on in the Fossil repository on 2008-01-01, visit [http://www.fossil-scm.org/fossil/timeline?c=2008-01-01]. 7. Further to the previous two hints, there are lots of query parameters that you can add to timeline pages. The available query parameters are tersely documented [/help?cmd=/timeline | here]. 8. You can run "[/help?cmd=test-diff | fossil test-diff --tk $file1 $file2]" to get a pop-up window with side-by-side diffs of two files, even if neither of the two files is part of any Fossil repository. Note that this command is "test-diff", not "diff". 9. On web pages showing the content of a file (for example [http://www.fossil-scm.org/fossil/artifact/c7dd1de9f]) you can manually add a query parameter of the form "ln=FROM,TO" to the URL that will cause the range of lines indicated to be highlighted. This is useful in pointing out a few lines of code using a hyperlink in an email or text message. Example: [http://www.fossil-scm.org/fossil/artifact/c7dd1de9f?ln=28,30]. Adding the "ln" query parameter without any argument simply turns on line numbers. This feature only works right with files with a mimetype of text/plain, of course. 10. When editing documentation to be checked in as managed files, you can preview what the documentation will look like by using the special "ckout" branch name in the "doc" URL while running "fossil ui". See the [./embeddeddoc.wiki | embedded documentation] for details. |
Changes to www/index.wiki.
|
| | | < < < < < | < < < < < < < < < > | | | < < < | < > | < | | | < < | | > | < < < < | < > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < < < < < < < < < | | | | < > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | <title>Home</title> <h3>What Is Fossil?</h3> <div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'> <ul> <li> [http://www.fossil-scm.org/download.html | Download] <li> [./quickstart.wiki | Quick Start] <li> [./build.wiki | Install] <li> [../COPYRIGHT-BSD2.txt | License] <li> [./faq.wiki | FAQ] <li> [./changes.wiki | Change Log] <li> [./hacker-howto.wiki | Hacker How-To] <li> [./hints.wiki | Tip & Hints] <li> [./permutedindex.html | Documentation Index] <li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book] <li> Mailing list <ul> <li> [http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/fossil-users | sign-up] <li> [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org | archives] </ul> </ul> <center><img src="fossil3.gif"></center> </div> <p>Fossil is a simple, high-reliability, distributed software configuration management system with these advanced features: 1. <b>Integrated Bug Tracking, Wiki, and Technotes</b> - In addition to doing [./concepts.wiki | distributed version control] like Git and Mercurial, Fossil also supports [./bugtheory.wiki | bug tracking], [./wikitheory.wiki | wiki], and [./event.wiki | technotes]. 2. <b>Built-in Web Interface</b> - Fossil has a built-in and intuitive [./webui.wiki | web interface] with a rich assortment of information pages ([./webpage-ex.md|examples]) designed to promote situational awareness. This entire website¹ is just a running instance of Fossil. The pages you see here are all [./wikitheory.wiki | wiki] or [./embeddeddoc.wiki | embedded documentation]. When you clone Fossil from one of its [./selfhost.wiki | self-hosting repositories], you get more than just source code - you get this entire website. <span style='font-size:75%;'>(¹except the [http://www.fossil-scm.org/download.html | download] page)</span> 3. <b>Self-Contained</b> - Fossil is a single self-contained stand-alone executable. To install, simply download a <a href="http://www.fossil-scm.org/download.html">precompiled binary</a> for Linux, Mac, OpenBSD, or Windows and put it on your $PATH. [./build.wiki | Easy-to-compile source code] is also available. 4. <b>Simple Networking</b> - No custom protocols or TCP ports. Fossil uses ordinary HTTP (or HTTPS or SSH) for network communications, so it works fine from behind restrictive firewalls, including [./quickstart.wiki#proxy|proxies]. The protocol is [./stats.wiki | bandwidth efficient] to the point that Fossil can be used comfortably over dial-up. 5. <b>CGI/SCGI Enabled</b> - No server is required, but if you want to set one up, Fossil supports four easy [./server.wiki | server configurations]. 6. <b>Autosync</b> - Fossil supports [./concepts.wiki#workflow | "autosync" mode] which helps to keep projects moving forward by reducing the amount of needless [./branching.wiki | forking and merging] often associated with distributed projects. 7. <b>Robust & Reliable</b> - Fossil stores content using an [./fileformat.wiki | enduring file format] in an SQLite database so that transactions are atomic even if interrupted by a power loss or system crash. Automatic [./selfcheck.wiki | self-checks] verify that all aspects of the repository are consistent prior to each commit. 8. <b>Free and Open-Source</b> - Uses the [../COPYRIGHT-BSD2.txt|2-clause BSD license]. <hr> <h3>Links For Fossil Users:</h3> * "Fuel" is cross-platform GUI front-end for Fossil written in Qt. [http://fuelscm.org/]. Fuel is an independent project run by a different group of developers. * [./reviews.wiki | Testimonials] from satisfied fossil users and [./quotes.wiki | Quotes] about Fossil and other DVCSes. * [./faq.wiki | FAQ] * The [./concepts.wiki | concepts] behind fossil * [./quickstart.wiki | Quick Start] guide to using fossil * [./qandc.wiki | Questions & Criticisms] directed at fossil. * [./build.wiki | Compiling and Installing] |
︙ | ︙ | |||
153 154 155 156 157 158 159 | * Methods to [./checkin_names.wiki | identify a specific check-in]. * [./inout.wiki | Import and export] from and to Git. * [./fossil-v-git.wiki | Fossil versus Git]. * [./fiveminutes.wiki | Up and running in 5 minutes as a single user] (contributed by Gilles Ganault on 2013-01-08). * [./antibot.wiki | How Fossil defends against abuse by spiders and bots]. | | | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | * Methods to [./checkin_names.wiki | identify a specific check-in]. * [./inout.wiki | Import and export] from and to Git. * [./fossil-v-git.wiki | Fossil versus Git]. * [./fiveminutes.wiki | Up and running in 5 minutes as a single user] (contributed by Gilles Ganault on 2013-01-08). * [./antibot.wiki | How Fossil defends against abuse by spiders and bots]. <h3>Links For Fossil Developers:</h3> * [./contribute.wiki | Contributing] code or documentation to the Fossil project. * [./theory1.wiki | Thoughts On The Design Of Fossil]. * [./pop.wiki | Principles Of Operation] * [./tech_overview.wiki | A Technical Overview Of Fossil]. * The [./fileformat.wiki | file format] used by every content file stored in the repository. * The [./delta_format.wiki | format of deltas] used to efficiently store changes between file revisions. * The [./delta_encoder_algorithm.wiki | encoder algorithm] used to efficiently generate deltas. * The [./sync.wiki | synchronization protocol]. |
Changes to www/inout.wiki.
︙ | ︙ | |||
45 46 47 48 49 50 51 | those concepts. As with the "import" command, the --git option is not required since the git-fast-export file format is currently the only VCS interchange format that Fossil will generate. However, future versions of Fossil might add the ability to generate other VCS interchange formats, and so for compatibility, the use of the --git | | > > > > > > > > > > > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | those concepts. As with the "import" command, the --git option is not required since the git-fast-export file format is currently the only VCS interchange format that Fossil will generate. However, future versions of Fossil might add the ability to generate other VCS interchange formats, and so for compatibility, the use of the --git option recommended. An anonymous user sends this comment: <blockquote> The main Fossil branch is called "trunk", while the main git branch is called "master". After you've exported your FOSSIL repo to git, you won't see any files and gitk will complain about a missing "HEAD". You can resolve this problem by merging "trunk" with "master" (first verify using git status that you are on the "master" branch): <tt>git merge trunk</tt> </blockquote> |
Changes to www/makefile.wiki.
︙ | ︙ | |||
40 41 42 43 44 45 46 | 6. shell.c The sqlite3.c and sqlite3.h source files are byte-for-byte copies of a standard [http://www.sqlite.org/amalgamation.html | amalgamation]. The shell.c source file is code for the SQLite [http://www.sqlite.org/sqlite.html | command-line shell] that is used to help implement the [/help/sqlite3 | fossil sql] command. The | | | < > > > > > > > | | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | 6. shell.c The sqlite3.c and sqlite3.h source files are byte-for-byte copies of a standard [http://www.sqlite.org/amalgamation.html | amalgamation]. The shell.c source file is code for the SQLite [http://www.sqlite.org/sqlite.html | command-line shell] that is used to help implement the [/help/sqlite3 | fossil sql] command. The shell.c source file is also a byte-for-byte copy of the shell.c file from the SQLite release. The TH1 script engine is implemented using files: 7. th.c 8. th.h These two files are imports like the SQLite source files, and so are not preprocessed. The VERSION.h header file is generated from other information sources using a small program called: 9. mkversion.c The builtin_data.h header file contains the definitions of C-language byte-array constants that contain various resources such as scripts and images. The builtin_data.h header file is generate from the original resource files using a small program called: 10 mkbuiltin.c The src/ subdirectory also contains documentation about the makeheaders preprocessor program: 11. [../src/makeheaders.html | makeheaders.html] Click on the link to read this documentation. In addition there is a [http://www.tcl.tk/ | Tcl] script used to build the various makefiles: 12. makemake.tcl Running this Tcl script will automatically regenerate all makefiles. In order to add a new source file to the Fossil implementation, simply edit makemake.tcl to add the new filename, then rerun the script, and all of the makefiles for all targets will be rebuild. Finally, there is one of the makefiles generated by makemake.tcl: 13. main.mk The main.mk makefile is invoked from the Makefile in the top-level directory. The main.mk is generated by makemake.tcl and should not be hand edited. Other makefiles generated by makemake.tcl are in other subdirectories (currently all in the win/ subdirectory). All the other files in the src/ subdirectory (79 files at the time of |
︙ | ︙ | |||
104 105 106 107 108 109 110 | updated by Fossil itself. See the [/help/setting | fossil set manifest] command for additional information.) The VERSION.h header file is generated by a C program: src/mkversion.c. To run the VERSION.h generator, first compile the src/mkversion.c source file into a command-line program (named "mkversion.exe") | | > > > > > > > > > > > > > > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | updated by Fossil itself. See the [/help/setting | fossil set manifest] command for additional information.) The VERSION.h header file is generated by a C program: src/mkversion.c. To run the VERSION.h generator, first compile the src/mkversion.c source file into a command-line program (named "mkversion.exe") then run: <blockquote><pre> mkversion.exe manifest.uuid manifest VERSION >VERSION.h </pre></blockquote> The pathnames in the above command might need to be adjusted to get the directories right. The point is that the manifest.uuid, manifest, and VERSION files in the root of the source tree are the three arguments and the generated VERSION.h file appears on standard output. The builtin_data.h header file is generated by a C program: src/mkbuiltin.c. The builtin_data.h file contains C-langauge byte-array definitions for the content of resource files used by Fossil. To generate the builtin_data.h file, first compile the mkbuiltin.c program, then run: <blockquote><pre> mkbuiltin.exe diff.tcl <i>OtherFiles...</i> >builtin_data.h </pre></blockquote> At the time of this writing, the "diff.tcl" script (a Tcl/Tk script used to generate implement --tk option on the diff command) is the only resource file processed using mkbuiltin.exe. However, new resources will likely be added using this facility in future versions of Fossil. <a name="preprocessing"></a> <h1>4.0 Preprocessing</h1> There are three preprocessors for the Fossil sources. The mkindex and translate preprocessors can be run in any order. The makeheaders preprocessor must be run after translate. |
︙ | ︙ | |||
201 202 203 204 205 206 207 | all at once, or each preprocessed source file can be compiled into a separate object code file and the resulting object code files linked together in a final step. Some files require special C-preprocessor macro definitions. When compiling sqlite.c, the following macros are recommended: | < > | | < < < | | | > > | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | all at once, or each preprocessed source file can be compiled into a separate object code file and the resulting object code files linked together in a final step. Some files require special C-preprocessor macro definitions. When compiling sqlite.c, the following macros are recommended: * -DSQLITE_OMIT_LOAD_EXTENSION=1 * -DSQLITE_ENABLE_FTS4=1 * -DSQLITE_ENABLE_LOCKING_STYLE=0 * -DSQLITE_THREADSAFE=0 * -DSQLITE_DEFAULT_FILE_FORMAT=4 * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1 The first two symbol definitions above are required; the others are merely recommended. Extension loading is omitted as a security measure. FTS4 is needed for the search feature. Fossil is single-threaded so mutexing is disabled in SQLite as a performance enhancement. The SQLITE_ENABLE_EXPLAIN_COMMENTS option makes the output of "EXPLAIN" queries in the "[/help?cmd=sqlite3|fossil sql]" command much more readable. When compiling the shell.c source file, these macros are required: * -Dmain=sqlite3_main * -DSQLITE_OMIT_LOAD_EXTENSION=1 The "main()" routine in the shell must be changed into sqlite3_main() |
︙ | ︙ |
Changes to www/mkdownload.tcl.
1 2 3 4 5 6 7 8 9 | #!/usr/bin/tclsh # # Run this script to build the "download.html" page. Also generate # the fossil_download_checksums.html page. # # set out [open download.html w] fconfigure $out -encoding utf-8 -translation lf puts $out \ | | > | | | > > | | | > | | | < < | < < < | < | | | | > | | < | | | < < < < < | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | #!/usr/bin/tclsh # # Run this script to build the "download.html" page. Also generate # the fossil_download_checksums.html page. # # set out [open download.html w] fconfigure $out -encoding utf-8 -translation lf puts $out \ {<!DOCTYPE html> <html> <head> <base href="https://www.fossil-scm.org/download.html" /> <title>Fossil: Download</title> <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="/fossil/timeline.rss" /> <link rel="stylesheet" href="/fossil/style.css?default" type="text/css" media="screen" /> </head> <body> <div class="header"> <div class="title"><h1>Fossil</h1>Download</div> </div> <div class="mainmenu"> <a href='/fossil/doc/trunk/www/index.wiki'>Home</a> <a href='/fossil/timeline?y=ci'>Timeline</a> <a href='/fossil/dir?ci=tip'>Code</a> <a href='/fossil/doc/trunk/www/permutedindex.html'>Docs</a> <a href='/fossil/brlist'>Branches</a> <a href='/fossil/ticket'>Tickets</a> <a href='/fossil/wiki'>Wiki</a> <a href='/download.html' class='active'>Download</a> </div> <div class="content"> <p> <center><font size=4>} puts $out \ "<b>To install Fossil →</b> download the stand-alone executable" puts $out \ {and put it on your $PATH. </font><p><small> RPMs available <a href="http://download.opensuse.org/repositories/home:/rmax:/fossil/"> here.</a> Cryptographic checksums for download files are <a href="http://www.hwaci.com/fossil_download_checksums.html">here</a>. </small></p> </center> <table cellpadding="10"> } # Find all all unique timestamps. # foreach file [glob -nocomplain download/fossil-*.zip] { if {[regexp -- {-(\d\.\d+).zip$} $file all version]} { set avers($version) 1 } } # Do all versions from newest to oldest # foreach vers [lsort -decr -real [array names avers]] { set hr "/fossil/timeline?c=version-$vers;y=ci" puts $out "<tr><td colspan=6 align=left><hr>" puts $out "<center><b><a href=\"$hr\">Version $vers</a></b></center>" puts $out "</td></tr>" puts $out "<tr>" foreach {prefix suffix img desc} { fossil-linux-x86 zip linux.gif {Linux 3.x x86} fossil-macosx-x86 zip mac.gif {Mac 10.x x86} fossil-openbsd-x86 zip openbsd.gif {OpenBSD 4.x x86} fossil-w32 zip win32.gif {Windows} fossil-src tar.gz src.gif {Source Tarball} } { set filename download/$prefix-$vers.$suffix if {[file exists $filename]} { set size [file size $filename] set units bytes if {$size>1024*1024} { set size [format %.2f [expr {$size/(1024.0*1024.0)}]] set units MiB } elseif {$size>1024} { set size [format %.2f [expr {$size/(1024.0)}]] set units KiB } puts $out "<td align=center valign=bottom><a href=\"$filename\">" puts $out "<img src=\"build-icons/$img\" border=0><br>$desc</a><br>" puts $out "$size $units</td>" } else { puts $out "<td> </td>" } } puts $out "</tr>" if {[file exists download/releasenotes-$vers.html]} { puts $out "<tr><td colspan=6 align=left>" set rn [open download/releasenotes-$vers.html] fconfigure $rn -encoding utf-8 puts $out "[read $rn]" close $rn puts $out "</td></tr>" } } puts $out "<tr><td colspan=5><hr></td></tr>" puts $out {</table></div> </body> </html> } close $out # Generate the checksum page # set out [open fossil_download_checksums.html w] fconfigure $out -encoding utf-8 -translation lf puts $out {<html> <title>Fossil Download Checksums</title> <body> <h1 align="center">Checksums For Fossil Downloads</h1> <p>The following table shows the SHA1 checksums for the precompiled binaries available on the <a href="/download.html">Fossil website</a>.</p> <pre>} foreach file [lsort [glob -nocomplain download/fossil-*.zip]] { set sha1sum [lindex [exec sha1sum $file] 0] puts $out "$sha1sum [file tail $file]" } puts $out {</pre></body></html>} close $out |
Changes to www/mkindex.tcl.
1 2 | #!/bin/sh # | | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #!/bin/sh # # Run this TCL script to generate a WIKI page that contains a # permuted index of the various documentation files. # # tclsh mkindex.tcl >permutedindex.html # set doclist { adding_code.wiki {Adding New Features To Fossil} adding_code.wiki {Hacking Fossil} antibot.wiki {Defense against Spiders and Bots} bugtheory.wiki {Bug Tracking In Fossil} branching.wiki {Branching, Forking, Merging, and Tagging} build.wiki {Compiling and Installing Fossil} checkin_names.wiki {Check-in And Version Names} checkin.wiki {Check-in Checklist} changes.wiki {Fossil Changelog} copyright-release.html {Contributor License Agreement} concepts.wiki {Fossil Core Concepts} contribute.wiki {Contributing Code or Documentation To The Fossil Project} customskin.md {Theming: Customizing The Appearance of Web Pages} custom_ticket.wiki {Customizing The Ticket System} delta_encoder_algorithm.wiki {Fossil Delta Encoding Algorithm} delta_format.wiki {Fossil Delta Format} embeddeddoc.wiki {Embedded Project Documentation} event.wiki {Events} faq.wiki {Frequently Asked Questions} fileformat.wiki {Fossil File Format} |
︙ | ︙ | |||
53 54 55 56 57 58 59 60 61 62 63 64 65 66 | stats.wiki {Performance Statistics} style.wiki {Source Code Style Guidelines} ssl.wiki {Using SSL with Fossil} sync.wiki {The Fossil Sync Protocol} tech_overview.wiki {A Technical Overview Of The Design And Implementation Of Fossil} tech_overview.wiki {SQLite Databases Used By Fossil} tickets.wiki {The Fossil Ticket System} theory1.wiki {Thoughts On The Design Of The Fossil DVCS} webui.wiki {The Fossil Web Interface} wikitheory.wiki {Wiki In Fossil} } set permindex {} | > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | stats.wiki {Performance Statistics} style.wiki {Source Code Style Guidelines} ssl.wiki {Using SSL with Fossil} sync.wiki {The Fossil Sync Protocol} tech_overview.wiki {A Technical Overview Of The Design And Implementation Of Fossil} tech_overview.wiki {SQLite Databases Used By Fossil} th1.md {The TH1 Scripting Language} tickets.wiki {The Fossil Ticket System} theory1.wiki {Thoughts On The Design Of The Fossil DVCS} webui.wiki {The Fossil Web Interface} wikitheory.wiki {Wiki In Fossil} } set permindex {} |
︙ | ︙ | |||
75 76 77 78 79 80 81 | set firstword [string tolower [lindex $suffix 0]] if {[lsearch $stopwords $firstword]<0} { lappend permindex [list "$suffix — $prefix" $file] } } } set permindex [lsort -dict -index 0 $permindex] | | > | > > > > > > | | | | | > | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | set firstword [string tolower [lindex $suffix 0]] if {[lsearch $stopwords $firstword]<0} { lappend permindex [list "$suffix — $prefix" $file] } } } set permindex [lsort -dict -index 0 $permindex] set out [open permutedindex.html w] fconfigure $out -encoding utf-8 -translation lf puts $out \ "<div class='fossil-doc' data-title='Index Of Fossil Documentation'>" puts $out { <center> <form action='../../../docsrch' method='GET'> <input type="text" name="s" size="40" autofocus> <input type="submit" value="Search Docs"> </form> </center> <h2>Primary Documents:</h2> <ul> <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='faq.wiki'>FAQ</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's book</a> <li> <a href='../../../help'>Command-line help</a> </ul> <a name="pindex"></a> <h2>Permuted Index:</h2> <ul>} foreach entry $permindex { foreach {title file} $entry break puts $out "<li><a href=\"$file\">$title</a></li>" } puts $out "</ul></div>" |
Changes to www/password.wiki.
1 2 3 4 5 6 7 8 9 10 11 | <title>Fossil Password Management</title> <h1 align="center">Password Management</h1> Fossil handles user authentication using passwords. Passwords are unique to each repository. Passwords are not part of the persistent state of a project. Passwords are not versioned and are not transmitted from one repository to another during a sync. Passwords are local configuration information that can (and usually does) vary from one repository to the next within the same project. Passwords are stored in the PW field of the USER table. | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <title>Fossil Password Management</title> <h1 align="center">Password Management</h1> Fossil handles user authentication using passwords. Passwords are unique to each repository. Passwords are not part of the persistent state of a project. Passwords are not versioned and are not transmitted from one repository to another during a sync. Passwords are local configuration information that can (and usually does) vary from one repository to the next within the same project. Passwords are stored in the PW field of the USER table. In older versions of Fossil (prior to [/timeline?c=2010-01-10T20:56:30 | 2010-01-11]) the password is stored as cleartext. In newer versions of Fossil, the password can be either cleartext or an SHA1 hash (written as a 40-character lower-case hexadecimal number). If the USER.PW field contains a 40-character string, that string is assumed to be a SHA1 hash. If the size of USER.PW is anything other than 40 characters, then it is understood as a plain-text password. |
︙ | ︙ | |||
69 70 71 72 73 74 75 | When a user logs into Fossil using the web interface, the login name and password are sent in the clear to the server. The server then hashes the password and compares it against the value stored in USER.PW. If they match, the server sets a cookie on the client to record the login. This cookie contains a large amount of high-quality randomness and is thus intractable to guess. The value of the cookie and the IP address of the client is stored in the USER.COOKIE and USER.IPADDR fields | | | | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | When a user logs into Fossil using the web interface, the login name and password are sent in the clear to the server. The server then hashes the password and compares it against the value stored in USER.PW. If they match, the server sets a cookie on the client to record the login. This cookie contains a large amount of high-quality randomness and is thus intractable to guess. The value of the cookie and the IP address of the client is stored in the USER.COOKIE and USER.IPADDR fields of the USER table on the server. The USER.CEXPIRE field holds an expiration date for the cookie, encoded as a julian day number. On all subsequent HTTP requests, the cookie value is matched against the USER table to enable access to the repository. A login cookie will only work if the IP address matches. This feature is designed to make it more difficult for an attacker to sniff the cookie and take over the connection. A cookie-sniffing attack will only work if the attacker is able to send and receive from the same IP address as the original login. However, we found that doing an exact IP match |
︙ | ︙ | |||
101 102 103 104 105 106 107 | login attempt. The plan moving forward is to compute the SHA1 hash of the password on the client using javascript and then send only the hash over the wire, but that plan has not yet been set in code. <h2>Sync Protocol Authentication</h2> A different authentication mechanism is used when one repository wants | | | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | login attempt. The plan moving forward is to compute the SHA1 hash of the password on the client using javascript and then send only the hash over the wire, but that plan has not yet been set in code. <h2>Sync Protocol Authentication</h2> A different authentication mechanism is used when one repository wants to sync (or push or pull or clone) another repository. When two repositories are syncing, the one that initiates the transaction is the client and the repository that responds is the server. The client works by sending HTTP requests to the server with a method of "xfer" and a content-type of "application/x-fossil". The content is Zlib-compressed text consisting of "cards" of instructions. The first card of this content is a "login" card responsible for authentication. The login card contains the login name of the user and a "signature" where the signature is the SHA1 hash of a nonce and the value of USER.PW. The nonce is the |
︙ | ︙ | |||
135 136 137 138 139 140 141 | The client normally gets the login and password from the "remote URL". <blockquote><pre> http://<font color="blue">login:password</font>@servername.org/path </pre></blockquote> For older clients, the password is used for the shared secret as stated | | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | The client normally gets the login and password from the "remote URL". <blockquote><pre> http://<font color="blue">login:password</font>@servername.org/path </pre></blockquote> For older clients, the password is used for the shared secret as stated in the URL and with no encoding. For newer clients, the shared secret is derived from the password by transformed the password using the SHA1 hash encoding described above. However, if the first character of the password is "*" (ASCII 0x2a) then the "*" is skipped and the rest of the password is used directly as the share secret without the SHA1 encoding. <blockquote><pre> http://<font color="blue">login:*password</font>@servername.org/path </pre></blockquote> This *-before-the-password trick can be used by newer clients to sync against a legacy server that does not understand the new SHA1 password encoding. |
Added www/permutedindex.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | <div class='fossil-doc' data-title='Index Of Fossil Documentation'> <center> <form action='../../../docsrch' method='GET'> <input type="text" name="s" size="40" autofocus> <input type="submit" value="Search Docs"> </form> </center> <h2>Primary Documents:</h2> <ul> <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='faq.wiki'>FAQ</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's book</a> <li> <a href='../../../help'>Command-line help</a> </ul> <a name="pindex"></a> <h2>Permuted Index:</h2> <ul> <li><a href="fiveminutes.wiki">5 Minutes as a Single User — Update and Running in</a></li> <li><a href="fossil-from-msvc.wiki">2010 IDE — Integrating Fossil in the Microsoft Express</a></li> <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> <li><a href="adding_code.wiki">Adding New Features To Fossil</a></li> <li><a href="antibot.wiki">against Spiders and Bots — Defense</a></li> <li><a href="copyright-release.html">Agreement — Contributor License</a></li> <li><a href="delta_encoder_algorithm.wiki">Algorithm — Fossil Delta Encoding</a></li> <li><a href="customskin.md">Appearance of Web Pages — Theming: Customizing The</a></li> <li><a href="fiveminutes.wiki">as a Single User — Update and Running in 5 Minutes</a></li> <li><a href="faq.wiki">Asked Questions — Frequently</a></li> <li><a href="password.wiki">Authentication — Password Management And</a></li> <li><a href="antibot.wiki">Bots — Defense against Spiders and</a></li> <li><a href="private.wiki">Branches — Creating, Syncing, and Deleting Private</a></li> <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li> <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li> <li><a href="makefile.wiki">Build Process — The Fossil</a></li> <li><a href="changes.wiki">Changelog — Fossil</a></li> <li><a href="checkin.wiki">Check-in Checklist</a></li> <li><a href="checkin_names.wiki">Check-in And Version Names</a></li> <li><a href="checkin.wiki">Checklist — Check-in</a></li> <li><a href="../test/release-checklist.wiki">Checklist — Pre-Release Testing</a></li> <li><a href="foss-cklist.wiki">Checklist For Successful Open-Source Projects</a></li> <li><a href="selfcheck.wiki">Checks — Fossil Repository Integrity Self</a></li> <li><a href="contribute.wiki">Code or Documentation To The Fossil Project — Contributing</a></li> <li><a href="style.wiki">Code Style Guidelines — Source</a></li> <li><a href="build.wiki">Compiling and Installing Fossil</a></li> <li><a href="concepts.wiki">Concepts — Fossil Core</a></li> <li><a href="server.wiki">Configure A Fossil Server — How To</a></li> <li><a href="shunning.wiki">Content From Fossil — Shunning: Deleting</a></li> <li><a href="contribute.wiki">Contributing Code or Documentation To The Fossil Project</a></li> <li><a href="copyright-release.html">Contributor License Agreement</a></li> <li><a href="concepts.wiki">Core Concepts — Fossil</a></li> <li><a href="newrepo.wiki">Create A New Fossil Repository — How To</a></li> <li><a href="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li> <li><a href="qandc.wiki">Criticisms — Questions And</a></li> <li><a href="customskin.md">Customizing The Appearance of Web Pages — Theming:</a></li> <li><a href="custom_ticket.wiki">Customizing The Ticket System</a></li> <li><a href="tech_overview.wiki">Databases Used By Fossil — SQLite</a></li> <li><a href="antibot.wiki">Defense against Spiders and Bots</a></li> <li><a href="shunning.wiki">Deleting Content From Fossil — Shunning:</a></li> <li><a href="private.wiki">Deleting Private Branches — Creating, Syncing, and</a></li> <li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm — Fossil</a></li> <li><a href="delta_format.wiki">Delta Format — Fossil</a></li> <li><a href="tech_overview.wiki">Design And Implementation Of Fossil — A Technical Overview Of The</a></li> <li><a href="theory1.wiki">Design Of The Fossil DVCS — Thoughts On The</a></li> <li><a href="embeddeddoc.wiki">Documentation — Embedded Project</a></li> <li><a href="contribute.wiki">Documentation To The Fossil Project — Contributing Code or</a></li> <li><a href="theory1.wiki">DVCS — Thoughts On The Design Of The Fossil</a></li> <li><a href="quotes.wiki">DVCSes in General — Quotes: What People Are Saying About Fossil, Git, and</a></li> <li><a href="embeddeddoc.wiki">Embedded Project Documentation</a></li> <li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm — Fossil Delta</a></li> <li><a href="event.wiki">Events</a></li> <li><a href="inout.wiki">Export To And From Git — Import And</a></li> <li><a href="fossil-from-msvc.wiki">Express 2010 IDE — Integrating Fossil in the Microsoft</a></li> <li><a href="adding_code.wiki">Features To Fossil — Adding New</a></li> <li><a href="fileformat.wiki">File Format — Fossil</a></li> <li><a href="branching.wiki">Forking, Merging, and Tagging — Branching,</a></li> <li><a href="delta_format.wiki">Format — Fossil Delta</a></li> <li><a href="fileformat.wiki">Format — Fossil File</a></li> <li><a href="changes.wiki">Fossil Changelog</a></li> <li><a href="concepts.wiki">Fossil Core Concepts</a></li> <li><a href="delta_encoder_algorithm.wiki">Fossil Delta Encoding Algorithm</a></li> <li><a href="delta_format.wiki">Fossil Delta Format</a></li> <li><a href="fileformat.wiki">Fossil File Format</a></li> <li><a href="quickstart.wiki">Fossil Quick Start Guide</a></li> <li><a href="selfcheck.wiki">Fossil Repository Integrity Self Checks</a></li> <li><a href="selfhost.wiki">Fossil Self Hosting Repositories</a></li> <li><a href="settings.wiki">Fossil Settings</a></li> <li><a href="hints.wiki">Fossil Tips And Usage Hints</a></li> <li><a href="fossil-v-git.wiki">Fossil Versus Git</a></li> <li><a href="quotes.wiki">Fossil, Git, and DVCSes in General — Quotes: What People Are Saying About</a></li> <li><a href="faq.wiki">Frequently Asked Questions</a></li> <li><a href="shunning.wiki">From Fossil — Shunning: Deleting Content</a></li> <li><a href="inout.wiki">From Git — Import And Export To And</a></li> <li><a href="quotes.wiki">General — Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li> <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> <li><a href="inout.wiki">Git — Import And Export To And From</a></li> <li><a href="quotes.wiki">Git, and DVCSes in General — Quotes: What People Are Saying About Fossil,</a></li> <li><a href="quickstart.wiki">Guide — Fossil Quick Start</a></li> <li><a href="style.wiki">Guidelines — Source Code Style</a></li> <li><a href="hacker-howto.wiki">Hacker How-To</a></li> <li><a href="adding_code.wiki">Hacking Fossil</a></li> <li><a href="hints.wiki">Hints — Fossil Tips And Usage</a></li> <li><a href="index.wiki">Home Page</a></li> <li><a href="selfhost.wiki">Hosting Repositories — Fossil Self</a></li> <li><a href="server.wiki">How To Configure A Fossil Server</a></li> <li><a href="newrepo.wiki">How To Create A New Fossil Repository</a></li> <li><a href="hacker-howto.wiki">How-To — Hacker</a></li> <li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> <li><a href="tech_overview.wiki">Implementation Of Fossil — A Technical Overview Of The Design And</a></li> <li><a href="inout.wiki">Import And Export To And From Git</a></li> <li><a href="build.wiki">Installing Fossil — Compiling and</a></li> <li><a href="fossil-from-msvc.wiki">Integrating Fossil in the Microsoft Express 2010 IDE</a></li> <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> <li><a href="webui.wiki">Interface — The Fossil Web</a></li> <li><a href="th1.md">Language — The TH1 Scripting</a></li> <li><a href="copyright-release.html">License Agreement — Contributor</a></li> <li><a href="password.wiki">Management And Authentication — Password</a></li> <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> <li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE — Integrating Fossil in the</a></li> <li><a href="fiveminutes.wiki">Minutes as a Single User — Update and Running in 5</a></li> <li><a href="checkin_names.wiki">Names — Check-in And Version</a></li> <li><a href="adding_code.wiki">New Features To Fossil — Adding</a></li> <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> <li><a href="pop.wiki">Operations — Principles Of</a></li> <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> <li><a href="index.wiki">Page — Home</a></li> <li><a href="customskin.md">Pages — Theming: Customizing The Appearance of Web</a></li> <li><a href="password.wiki">Password Management And Authentication</a></li> <li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General — Quotes: What</a></li> <li><a href="stats.wiki">Performance Statistics</a></li> <li><a href="../test/release-checklist.wiki">Pre-Release Testing Checklist</a></li> <li><a href="pop.wiki">Principles Of Operations</a></li> <li><a href="private.wiki">Private Branches — Creating, Syncing, and Deleting</a></li> <li><a href="makefile.wiki">Process — The Fossil Build</a></li> <li><a href="contribute.wiki">Project — Contributing Code or Documentation To The Fossil</a></li> <li><a href="embeddeddoc.wiki">Project Documentation — Embedded</a></li> <li><a href="foss-cklist.wiki">Projects — Checklist For Successful Open-Source</a></li> <li><a href="sync.wiki">Protocol — The Fossil Sync</a></li> <li><a href="faq.wiki">Questions — Frequently Asked</a></li> <li><a href="qandc.wiki">Questions And Criticisms</a></li> <li><a href="quickstart.wiki">Quick Start Guide — Fossil</a></li> <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> <li><a href="reviews.wiki">Reviews</a></li> <li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Update and</a></li> <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> <li><a href="th1.md">Scripting Language — The TH1</a></li> <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> <li><a href="settings.wiki">Settings — Fossil</a></li> <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> <li><a href="fiveminutes.wiki">Single User — Update and Running in 5 Minutes as a</a></li> <li><a href="style.wiki">Source Code Style Guidelines</a></li> <li><a href="antibot.wiki">Spiders and Bots — Defense against</a></li> <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> <li><a href="stats.wiki">Statistics — Performance</a></li> <li><a href="style.wiki">Style Guidelines — Source Code</a></li> <li><a href="foss-cklist.wiki">Successful Open-Source Projects — Checklist For</a></li> <li><a href="sync.wiki">Sync Protocol — The Fossil</a></li> <li><a href="private.wiki">Syncing, and Deleting Private Branches — Creating,</a></li> <li><a href="custom_ticket.wiki">System — Customizing The Ticket</a></li> <li><a href="tickets.wiki">System — The Fossil Ticket</a></li> <li><a href="branching.wiki">Tagging — Branching, Forking, Merging, and</a></li> <li><a href="tech_overview.wiki">Technical Overview Of The Design And Implementation Of Fossil — A</a></li> <li><a href="../test/release-checklist.wiki">Testing Checklist — Pre-Release</a></li> <li><a href="th1.md">TH1 Scripting Language — The</a></li> <li><a href="makefile.wiki">The Fossil Build Process</a></li> <li><a href="sync.wiki">The Fossil Sync Protocol</a></li> <li><a href="tickets.wiki">The Fossil Ticket System</a></li> <li><a href="webui.wiki">The Fossil Web Interface</a></li> <li><a href="th1.md">The TH1 Scripting Language</a></li> <li><a href="customskin.md">Theming: Customizing The Appearance of Web Pages</a></li> <li><a href="theory1.wiki">Thoughts On The Design Of The Fossil DVCS</a></li> <li><a href="custom_ticket.wiki">Ticket System — Customizing The</a></li> <li><a href="tickets.wiki">Ticket System — The Fossil</a></li> <li><a href="hints.wiki">Tips And Usage Hints — Fossil</a></li> <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> <li><a href="fiveminutes.wiki">Update and Running in 5 Minutes as a Single User</a></li> <li><a href="hints.wiki">Usage Hints — Fossil Tips And</a></li> <li><a href="fiveminutes.wiki">User — Update and Running in 5 Minutes as a Single</a></li> <li><a href="ssl.wiki">Using SSL with Fossil</a></li> <li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> <li><a href="webui.wiki">Web Interface — The Fossil</a></li> <li><a href="customskin.md">Web Pages — Theming: Customizing The Appearance of</a></li> <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> <li><a href="wikitheory.wiki">Wiki In Fossil</a></li> <li><a href="ssl.wiki">with Fossil — Using SSL</a></li> </ul></div> |
Deleted www/permutedindex.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to www/pop.wiki.
︙ | ︙ | |||
29 30 31 32 33 34 35 | collection of artifacts. Each artifact is named by its SHA1 hash encoded in lowercase hexadecimal. In many contexts, the name can be abbreviated to a unique prefix. A five- or six-character prefix usually suffices to uniquely identify a file.</p></li> <li><p>Because artifacts are named by their SHA1 hash, all artifacts | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | collection of artifacts. Each artifact is named by its SHA1 hash encoded in lowercase hexadecimal. In many contexts, the name can be abbreviated to a unique prefix. A five- or six-character prefix usually suffices to uniquely identify a file.</p></li> <li><p>Because artifacts are named by their SHA1 hash, all artifacts are immutable. Any change to the content of an artifact also changes the hash that forms the artifacts name, thus creating a new artifact. Both the old original version of the artifact and the new change are preserved under different names.</p></li> <li><p>It is theoretically possible for two artifacts with different content to share the same hash. But finding two such artifacts is so incredibly difficult and unlikely that we |
︙ | ︙ |
Changes to www/private.wiki.
︙ | ︙ | |||
14 15 16 17 18 19 20 | fossil commit --private </pre></blockquote> The --private option causes Fossil to put the check-in in a new branch named "private". That branch will not participate in subsequent clone, sync, push, or pull operations. The branch will remain on the one local repository where it was created. Note that you only use the --private | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | fossil commit --private </pre></blockquote> The --private option causes Fossil to put the check-in in a new branch named "private". That branch will not participate in subsequent clone, sync, push, or pull operations. The branch will remain on the one local repository where it was created. Note that you only use the --private option for the first check-in that creates the private branch. Additional checkins into the private branch remain private automatically. <h2>Publishing Private Changes</h2> After additional work, one might desire to publish the changes associated with a private branch. The usual way to do this is to merge those changes into a public branch. For example: |
︙ | ︙ | |||
39 40 41 42 43 44 45 | visible to other users of the project. <h2>Syncing Private Branches</h2> A private branch normally stays on the one repository where it was originally created. But sometimes you want to share private branches with another repository. For example, you might be building a cross-platform | | | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | visible to other users of the project. <h2>Syncing Private Branches</h2> A private branch normally stays on the one repository where it was originally created. But sometimes you want to share private branches with another repository. For example, you might be building a cross-platform application and have separate repositories on your Windows laptop, your Linux desktop, and your iMac. You can transfer private branches between these machines by using the --private option on the "sync", "push", "pull", and "clone" commands. For example, if you are running "fossil server" on your Linux box and you want to clone that repository to your Mac, including all private branches, use: <blockquote><pre> fossil clone --private http://user@linux.localnetwork:8080/ mac-clone.fossil </pre></blockquote> You'll have to supply a username and password in order for this to work. |
︙ | ︙ |
Changes to www/qandc.wiki.
︙ | ︙ | |||
144 145 146 147 148 149 150 | <p>As for bloat: Fossil is a single self-contained executable. You do not need any other packages (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, sqlite, and so forth) in order to run fossil. Fossil runs just fine in a chroot jail all by itself. And the self-contained fossil | | > > | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | <p>As for bloat: Fossil is a single self-contained executable. You do not need any other packages (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, sqlite, and so forth) in order to run fossil. Fossil runs just fine in a chroot jail all by itself. And the self-contained fossil executable is much less than 1MB in size. (Update 2015-01-12: Fossil has grown in the years since the previous sentence was written but is still much less than 2MB according to "size" when compiled using -Os on x64 Linux.) Fossil is the very opposite of bloat.</p> </blockquote> </nowiki> |
Changes to www/quickstart.wiki.
︙ | ︙ | |||
53 54 55 56 57 58 59 | <blockquote> <b>fossil clone</b> <i>URL repository-filename</i> </blockquote> <p>The <i>URL</i> above is the http URL for the fossil repository you want to clone, and it may include a "user:password" part, e.g. | | | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | <blockquote> <b>fossil clone</b> <i>URL repository-filename</i> </blockquote> <p>The <i>URL</i> above is the http URL for the fossil repository you want to clone, and it may include a "user:password" part, e.g. <tt>http://user:password@www.fossil-scm.org/fossil</tt>. You can call the new repository anything you want - there are no naming restrictions. As an example, you can clone the fossil repository this way:</p> <blockquote> <b>fossil clone http://www.fossil-scm.org/ myclone.fossil</b> </blockquote> <p>The new local copy of the repository is stored in a single file, which in the example above is named "myclone.fossil". You can name your repositories anything you want. The ".fossil" suffix is not required.</p> <p>Note: If you are behind a restrictive firewall, you might need to <a href="#proxy">specify an HTTP proxy</a>.</p> <p>A Fossil repository is a single disk file. Instead of cloning, you can just make a copy of the repository file (for example, using "scp"). Note, however, that the repository file contains auxiliary information above and beyond the versioned files, including some sensitive information such as password hashes and email addresses. If you want to share Fossil repositories directly, consider running the [/help/scrub|fossil scrub] command to remove sensitive information before transmitting the file. <h2>Importing From Another Version Control System</h2> <p>Rather than start a new project, or clone an existing Fossil project, |
︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 127 128 129 130 | <b>[/help/branch | fossil branch]</b><br> </blockquote> <p>Note that Fossil allows you to make multiple check-outs in separate directories from the same repository. This enables you, for example, to do builds from multiple branches or versions at the same time without having to generate extra clones.</p> <h2>Configuring Your Local Repository</h2> <p>When you create a new repository, either by cloning an existing project or create a new project of your own, you usually want to do some local configuration. This is easily accomplished using the web-server that is built into fossil. Start the fossil webserver like this: | > > > > > > > > > > > > > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | <b>[/help/branch | fossil branch]</b><br> </blockquote> <p>Note that Fossil allows you to make multiple check-outs in separate directories from the same repository. This enables you, for example, to do builds from multiple branches or versions at the same time without having to generate extra clones.</p> <p>To switch a checkout between different versions and branches, use:</p> <blockquote> <b>[/help/update | fossil update]</b><br> <b>[/help/checkout | fossil checkout]</b><br> </blockquote> <p>[/help/update | update] honors the "autosync" option and does a "soft" switch, merging any local changes into the target version, whereas [/help/checkout | checkout] does not automatically sync and does a "hard" switch, overwriting local changes if told to do so.</p> <h2>Configuring Your Local Repository</h2> <p>When you create a new repository, either by cloning an existing project or create a new project of your own, you usually want to do some local configuration. This is easily accomplished using the web-server that is built into fossil. Start the fossil webserver like this: |
︙ | ︙ | |||
218 219 220 221 222 223 224 | abbreviation to the 40-character artifact identifier for a particular check-in, or it can be a date/time stamp. ([./checkin_names.wiki | more info]) If you omit the <i>VERSION</i>, then fossil moves you to the latest version of the branch your are currently on.</p> | | > > > > > > > > > | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | abbreviation to the 40-character artifact identifier for a particular check-in, or it can be a date/time stamp. ([./checkin_names.wiki | more info]) If you omit the <i>VERSION</i>, then fossil moves you to the latest version of the branch your are currently on.</p> <p>The default behavior is for [./concepts.wiki#workflow|autosync] to be turned on. That means that a [/help/pull|pull] automatically occurs when you run [/help/update|update] and a [/help/push|push] happens automatically after you [/help/commit|commit]. So in normal practice, the push, pull, and sync commands are rarely used. But it is important to know about them, all the same.</p> <blockquote> <b>[/help/checkout | fossil checkout]</b> <i>VERSION</i> </blockquote> <p>Is similar to update except that it does not honor the autosync setting, nor does it merge in local changes - it prefers to overwrite them and fails if local changes exist unless the <tt>--force</tt> flag is used.</p> <h2>Branching And Merging</h2> <p>Use the --branch option to the [/help/commit | commit] command to start a new branch. Note that in Fossil, branches are normally created when you commit, not before you start editing. You can use the [/help/branch | branch new] command to create a new branch before you start editing, if you want, but most people just wait |
︙ | ︙ | |||
311 312 313 314 315 316 317 | <p>Servers are also easily configured as: <ul> <li>[./server.wiki#inetd|inetd/xinetd] <li>[./server.wiki#cgi|CGI] <li>[./server.wiki#scgi|SCGI] </ul> | | | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | <p>Servers are also easily configured as: <ul> <li>[./server.wiki#inetd|inetd/xinetd] <li>[./server.wiki#cgi|CGI] <li>[./server.wiki#scgi|SCGI] </ul> <p>The [./selfhost.wiki | self-hosting fossil repositories] use CGI. <a name="proxy"></a> <h2>HTTP Proxies</h2> <p>If you are behind a restrictive firewall that requires you to use an HTTP proxy to reach the internet, then you can configure the proxy |
︙ | ︙ | |||
362 363 364 365 366 367 368 | <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> </blockquote> <h2>More Hints</h2> <p>A [/help | complete list of commands] is available, as is the [./hints.wiki|helpful hints] document. See the | | | 385 386 387 388 389 390 391 392 393 394 395 | <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> </blockquote> <h2>More Hints</h2> <p>A [/help | complete list of commands] is available, as is the [./hints.wiki|helpful hints] document. See the [./permutedindex.html#pindex|permuted index] for additional documentation. <p>Explore and have fun!</p> |
Changes to www/quotes.wiki.
1 2 3 4 5 6 7 8 9 | <title>What People Are Saying</title> The following are collected quotes from various forums and blogs about Fossil, Git, and DVCSes in general. This collection is put together by the creator of Fossil, so of course there is selection bias... <h2>On The Usability Of Git:</h2> <ol> | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <title>What People Are Saying</title> The following are collected quotes from various forums and blogs about Fossil, Git, and DVCSes in general. This collection is put together by the creator of Fossil, so of course there is selection bias... <h2>On The Usability Of Git:</h2> <ol> <li>Git approaches the usability of iptables, which is to say, utterly unusable unless you have the manpage tattooed on you arm. <blockquote> <i>by mml at [http://news.ycombinator.com/item?id=1433387]</i> </blockquote> <li><nowiki>It's simplest to think of the state of your [git] repository |
︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 66 67 | <li>Klingon Code Warriors embrace Git; we enjoy arbitrary conflicts. Git is not for the weak and feeble. TODAY IS A GOOD DAY TO CODE. <blockquote> <i>teastain at [http://www.reddit.com/r/programming/comments/xpitj/10_things_i_hate_about_git/c5oj4fk] </blockquote> </ol> <h2>On The Usability Of Fossil:</h2> <ol> | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | <li>Klingon Code Warriors embrace Git; we enjoy arbitrary conflicts. Git is not for the weak and feeble. TODAY IS A GOOD DAY TO CODE. <blockquote> <i>teastain at [http://www.reddit.com/r/programming/comments/xpitj/10_things_i_hate_about_git/c5oj4fk] </blockquote> <li>[G]it is <i>designed</i> to forget things. <blockquote> <i>[http://www.cs.cmu.edu/~davide/howto/git_lose.html] </blockquote> <li>[I]n nearly 31 years of using a computer i have, in total, lost more data to git (while following the instructions!!!) than any other single piece of software. <blockquote> <i>Stephen Beal on the [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg17181.html|Fossil mailing list] 2014-09-01.</i> </blockquote> <li>If programmers _really_ wanted to help scientists, they'd build a version control system that was more usable than Git. <blockquote> <i>Tweet by Greg Wilson @gvwilson on 2015-02-22 17:47</i> </blockquote> </ol> <h2>On The Usability Of Fossil:</h2> <ol> <li value=10> Fossil mesmerizes me with simplicity especially after I struggled to get a bug-tracking system to work with mercurial. <blockquote> <i>rawjeev at [http://stackoverflow.com/questions/156322/what-do-people-think-of-the-fossil-dvcs]</i> </blockquote> <li>Fossil is the best thing to happen to my development workflow this year, as I am pretty sure that using Git has resulted in the premature death of too many of my brain cells. I'm glad to be able to replace Git in every place that I possibly can with Fossil. <blockquote> <i>Joe Prostko at [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg16716.html] </blockquote> <li>Fossil is awesome!!! I have never seen an app like that before, such simplicity and flexibility!!! <blockquote> <i>zengr at [http://stackoverflow.com/questions/138621/best-version-control-for-lone-developer]</i> </blockquote> <li>This is my favourite VCS. I can carry it on a USB. And it's a complete system, with it's own server, ticketing system, Wiki pages, and a very, very helpful timeline visualization. And the entire program in a single file! <blockquote> <i>thunderbong commenting on hacker news: [https://news.ycombinator.com/item?id=9131619]</i> </blockquote> </ol> <h2>On Git Versus Fossil</h2> <ol> <li value=14> Just want to say thanks for fossil making my life easier.... Also <nowiki>[for]</nowiki> not having a misanthropic command line interface. <blockquote> <i>Joshua Paine at [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg02736.html]</i> </blockquote> |
︙ | ︙ | |||
114 115 116 117 118 119 120 121 | the published aspect of the project, so it provides tools for rearranging that history so you can present what you "should" have done rather than what you actually did. <blockquote> <i>Mike Meyer on the Fossil mailing list, 2011-10-04</i> </blockquote> </ol> | > > > > > > | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | the published aspect of the project, so it provides tools for rearranging that history so you can present what you "should" have done rather than what you actually did. <blockquote> <i>Mike Meyer on the Fossil mailing list, 2011-10-04</i> </blockquote> <li>github is such a pale shadow of what fossil does. <blockquote> <i>dkf on the Tcl chatroom, 2013-12-06</i> </blockquote> </ol> |
Changes to www/scgi.wiki.
︙ | ︙ | |||
18 19 20 21 22 23 24 | scgi_pass localhost:9000; scgi_param SCRIPT_NAME "/demo_project"; } </pre></blockquote> Note that Nginx does not normally send either the PATH_INFO or SCRIPT_NAME variables via SCGI, but Fossil needs one or the other. So the configuration | | | 18 19 20 21 22 23 24 25 26 | scgi_pass localhost:9000; scgi_param SCRIPT_NAME "/demo_project"; } </pre></blockquote> Note that Nginx does not normally send either the PATH_INFO or SCRIPT_NAME variables via SCGI, but Fossil needs one or the other. So the configuration above needs to add SCRIPT_NAME. If you do not do this, Fossil returns an error. |
Changes to www/selfcheck.wiki.
︙ | ︙ | |||
42 43 44 45 46 47 48 | zlib-compression which is relatively well-tested but still might cause corruption if used improperly. And there is the relatively new delta-encoding mechanism designed expressly for fossil. We want to make sure that bugs in these encoding mechanisms do not lead to loss of data. To increase our confidence that everything in the repository is | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | zlib-compression which is relatively well-tested but still might cause corruption if used improperly. And there is the relatively new delta-encoding mechanism designed expressly for fossil. We want to make sure that bugs in these encoding mechanisms do not lead to loss of data. To increase our confidence that everything in the repository is recoverable, fossil makes sure it can extract an exact replica of every content file that it changes just prior to transaction commit. So during the course of check-in (or other repository operation) many different files in the repository might be modified. Some files are simply compressed. Other files are delta encoded and then compressed. While all this is going on, fossil makes a record of every file that is encoded and the SHA1 hash of the original content of that |
︙ | ︙ | |||
67 68 69 70 71 72 73 | files. <h2>Checksum Over All Files In A Check-in</h2> Manifest artifacts that define a check-in have two fields (the R-card and Z-card) that record MD5 hashes of the manifest itself and of all other files in the manifest. Prior to any check-in | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | files. <h2>Checksum Over All Files In A Check-in</h2> Manifest artifacts that define a check-in have two fields (the R-card and Z-card) that record MD5 hashes of the manifest itself and of all other files in the manifest. Prior to any check-in commit, these checksums are verified to ensure that the check-in agrees exactly with what is on disk. Similarly, the repository checksum is verified after a checkout to make sure that the entire repository was checked out correctly. Note that these added checks use a different hash (MD5 instead of SHA1) in order to avoid common-mode failures in the hash algorithm implementation. |
︙ | ︙ |
Changes to www/selfhost.wiki.
︙ | ︙ | |||
36 37 38 39 40 41 42 | </pre></blockquote> Server (3) runs as a CGI script on a shared hosting account at <a href="http://www.he.net/">Hurricane Electric</a> in Fremont, CA. This server demonstrates the ability of Fossil to run on an economical shared-host web account with no privileges beyond port 80 HTTP access and CGI. It is not necessary | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | </pre></blockquote> Server (3) runs as a CGI script on a shared hosting account at <a href="http://www.he.net/">Hurricane Electric</a> in Fremont, CA. This server demonstrates the ability of Fossil to run on an economical shared-host web account with no privileges beyond port 80 HTTP access and CGI. It is not necessary to have a dedicated computer with administrator privileges to run Fossil. As far as we are aware, Fossil is the only full-featured configuration management system that can run in such a restricted environment. The CGI script that runs on the Hurricane Electric server is the same as the CGI script shown above, except that the pathnames are modified to suit the environment: |
︙ | ︙ |
Changes to www/server.wiki.
︙ | ︙ | |||
51 52 53 54 55 56 57 | their behavior. See the [/help/server|online documentation] for an overview. </p> </blockquote> <a name="inetd"></a> <h2>Fossil as an inetd/xinetd or stunnel service</h2><blockquote> <p> A Fossil server can be launched on-demand by inetd or xinetd using | | | | > > > > > > > > > > > > > > > > | | | > > > > | | | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | their behavior. See the [/help/server|online documentation] for an overview. </p> </blockquote> <a name="inetd"></a> <h2>Fossil as an inetd/xinetd or stunnel service</h2><blockquote> <p> A Fossil server can be launched on-demand by inetd or xinetd using the [/help/http|fossil http] command. To launch Fossil from inetd, modify your inetd configuration file (typically "/etc/inetd.conf") to contain a line something like this: <blockquote> <pre> 80 stream tcp nowait.1000 root /usr/bin/fossil /usr/bin/fossil http /home/fossil/repo.fossil </pre> </blockquote> In this example, you are telling "inetd" that when an incoming connection appears on TCP port "80", that it should launch the binary "/usr/bin/fossil" program with the arguments shown. Obviously you will need to modify the pathnames for your particular setup. The final argument is either the name of the fossil repository to be served, or a directory containing multiple repositories. </p> <p> If you use a non-standard TCP port on systems where the port-specification must be a symbolic name and cannot be numeric, add the desired name and port to /etc/services. For example, if you want your Fossil server running on TCP port 12345 instead of 80, you will need to add: <blockquote> <pre> fossil 12345/tcp #fossil server </pre> </blockquote> and use the symbolic name ('fossil' in this example) instead of the numeral ('12345') in inetd.conf. For details, see the relevant section in your system's documentation, e.g. the [https://www.freebsd.org/doc/en/books/handbook/network-inetd.html|FreeBSD Handbook] in case you use FreeBSD. </p> <p> If your system is running xinetd, then the configuration is likely to be in the file "/etc/xinetd.conf" or in a subfile of "/etc/xinetd.d". An xinetd configuration file will appear like this:</p> <blockquote> <pre> service http { port = 80 socket_type = stream wait = no user = root server = /usr/bin/fossil server_args = http /home/fossil/repos/ } </pre> </blockquote> <p> The xinetd example above has Fossil configured to serve multiple repositories, contained under the "/home/fossil/repos/" directory. </p> <p> In both cases notice that Fossil was launched as root. This is not required, but if it is done, then Fossil will automatically put itself into a chroot jail for the user who owns the fossil repository before reading any information off of the wire. </p> <p> Inetd or xinetd must be enabled, and must be (re)started whenever their configuration changes - consult your system's documentation for details. </p> <p> [https://www.stunnel.org/ | Stunnel version 5] is an inetd-like process that accepts and decodes SSL-encrypted connections. Fossil can be run directly from stunnel in a manner similar to inetd and xinetd. This can be used to provide a secure link to a Fossil project. The configuration needed to get stunnel5 to invoke Fossil is very similar to the inetd and xinetd examples shown above. The relevant parts of an stunnel configuration might look something like the following: <blockquote><pre><nowiki> [https] accept = www.ubercool-project.org:443 TIMEOUTclose = 0 exec = /usr/bin/fossil execargs = /usr/bin/fossil http /home/fossil/ubercool.fossil --https </nowiki></pre></blockquote> See the stunnel5 documentation for further details about the /etc/stunnel/stunnel.conf configuration file. Note that the [/help/http|fossil http] command should include the --https option to let Fossil know to use "https" instead of "http" as the scheme on generated hyperlinks. <p> Using inetd or xinetd or stunnel is a more complex setup than the "standalone" server, but it has the advantage of only using system resources when an actual connection is |
︙ | ︙ | |||
196 197 198 199 200 201 202 | starting a stand-alone web server, can also be used for SCGI. Simply add the --scgi command-line option and the stand-alone server will interpret and respond to the SimpleCGI or SCGI protocol rather than raw HTTP. This can be used in combination with a webserver (such as [http://nginx.org|Nginx]) that does not support CGI. A typical Nginx configuration to support SCGI with Fossil would look something like this: <blockquote><pre> | | > > > > > > > > > > | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | starting a stand-alone web server, can also be used for SCGI. Simply add the --scgi command-line option and the stand-alone server will interpret and respond to the SimpleCGI or SCGI protocol rather than raw HTTP. This can be used in combination with a webserver (such as [http://nginx.org|Nginx]) that does not support CGI. A typical Nginx configuration to support SCGI with Fossil would look something like this: <blockquote><pre> location /demo_project/ { include scgi_params; scgi_pass localhost:9000; scgi_param SCRIPT_NAME "/demo_project"; } </pre></blockquote> <p> Note that Fossil requires the SCRIPT_NAME variable in order to function properly, but Nginx does not provide this variable by default. So it is necessary to provide the SCRIPT_NAME parameter in the configuration. Failure to do this will cause Fossil to return an error. </p> <p> All of the features of the stand-alone server mode described above, such as the ability to serve a directory full of Fossil repositories rather than just a single repository, work the same way in SCGI mode. </p> <p> For security, it is probably a good idea to add the --localhost option to the [/help/server|fossil server] command to prevent Fossil from accepting off-site connections. And one might want to specify the listening TCP port number, rather than letting Fossil choose one for itself, just to avoid ambiguity. A typical command to start a Fossil SCGI server would be something like this: <blockquote><pre> fossil server $REPOSITORY --scgi --localhost --port 9000 </pre></blockquote> </blockquote> <h2>Securing a repository with SSL</h2><blockquote> <p> Using either CGI or SCGI, it is trivial to use SSL to secure the server. Simply set up the Fossil CGI scripts etc. as above, but modify the Apache (or IIS, etc.) server to require SSL (that is, a |
︙ | ︙ | |||
239 240 241 242 243 244 245 246 | support SSL. </p> <p> For more information, see <a href="./ssl.wiki">Using SSL with Fossil</a>. </p> </blockquote> </blockquote> | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | support SSL. </p> <p> For more information, see <a href="./ssl.wiki">Using SSL with Fossil</a>. </p> </blockquote> <a name="loadmgmt"></a> <h2>Managing Server Load</h2><blockquote> <p> A Fossil server is very efficient and normally presents a very light load on the server. The Fossil [./selfhost.wiki | self-hosting server] is a 1/24th slice VM at [http://www.linode.com | Linode.com] hosting 65 other repositories in addition to Fossil (and including some very high-traffic sites such as [http://www.sqlite.org] and [http://system.data.sqlite.org]) and it has a typical load of 0.05 to 0.1. A single HTTP request to Fossil normally takes less than 10 milliseconds of CPU time to complete. So requests can be arriving at a continuous rate of 20 or more per second and the CPU can still be mostly idle. <p> However, there are some Fossil web pages that can consume large amounts of CPU time, especially on repositories with a large number of files or with long revision histories. High CPU usage pages include [/help?cmd=/zip | /zip], [/help?cmd=/tarball | /tarball], [/help?cmd=/annotate | /annotate] and others. On very large repositories, these commands can take 15 seconds or more of CPU time. If these kinds of requests arrive too quickly, the load average on the server can grow dramatically, making the server unresponsive. <p> Fossil provides two capabilities to help avoid server overload problems due to excessive requests to expensive pages: <ol> <li><p>An optional cache is available that remembers the 10 most recently requested /zip or /tarball pages and returns the precomputed answer if the same page is requested again. <li><p>Page requests can be configured to fail with a [http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.3 | "503 Server Overload"] HTTP error if an expensive request is received while the host load average is too high. </ol> Both of these load-control mechanisms are turned off by default, but they are recommended for high-traffic sites. <p> The webpage cache is activated using the [/help?cmd=cache|fossil cache init] command-line on the server. Add a -R option to specify the specific repository for which to enable caching. If running this command as root, be sure to "chown" the cache database (which is a separate file in the same directory and with the same name as the repository but with the suffix changed to ".cache") to give it write permission for the userid of the webserver. <p> To activate the server load control feature visit the /Admin/Access setup page in the administrative web interface and in the "<b>Server Load Average Limit</b>" box enter the load average threshold above which "503 Server Overload" replies will be issued for expensive requests. On the self-host Fossil server, that value is set to 1.5. But you could easily set it higher on a multi-core server. <p> The maximum load average can also be set on the command line using commands like this: <blockquote><pre> fossil set max-loadavg 1.5 fossil all set max-loadavg 1.5 </pre></blockquote> The second form is especially useful for changing the maximum load average simultaneously on a large number of repositories. <p> Note that this load-average limiting feature is only available on operating systems that support the "getloadavg()" API. Most modern Unix systems have this interface, but Windows does not, so the feature will not work on Windows. Note also that Linux implements "getloadavg()" by accessing the "/proc/loadavg" file in the "proc" virtual filesystem. If you are running a Fossil instance inside a chroot() jail on Linux, you will need to make the "/proc" file system available inside that jail in order for this feature to work. On the self-hosting Fossil repository, this was accomplished by adding a line to the "/etc/mtab" file that looks like: <blockquote><pre> chroot_jail_proc /home/www/proc proc r 0 0 </pre></blockquote> Pathnames should be adjusted for individual systems, of course. <p> To see if the load-average limiter is functional, visit the [/test_env] page of the server to view the current load average. If the value for the load average is greater than zero, that means that it is possible to activate the load-average limiter on that repository. If the load average shows exactly "0.0", then that means that Fossil is unable to find the load average (either because it is in a chroot() jail without /proc access, or because it is running on a system that does not support "getloadavg()") and so the load-average limiter will not function. </blockquote> |
Changes to www/shunning.wiki.
︙ | ︙ | |||
32 33 34 35 36 37 38 | itself) are removed from the repository whenever the repository is reconstructed using the "rebuild" command. <h3>Shunning lists are local state</h3> The shunning list is part of the local state of a Fossil repository. | | | | | > | > > | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | itself) are removed from the repository whenever the repository is reconstructed using the "rebuild" command. <h3>Shunning lists are local state</h3> The shunning list is part of the local state of a Fossil repository. In other words, shunning does not propagate to a remote repository using the normal "sync" mechanism. An artifact can be shunned from one repository but be allowed to exist in another. The fact that the shunning list does not propagate is a security feature. If the shunning list propagated then a malicious user (or a bug in the fossil code) might introduce a shun record that would propagate through all repositories in a network and permanently destroy vital information. By refusing to propagate the shunning list, Fossil ensures that no remote user will ever be able to remove information from your personal repositories without your permission. The shunning list does not propagate to a remote repository by the normal "sync" mechanism, but it is still possible to copy shuns from one repository to another using the "configuration" command: <b>fossil configuration pull shun</b> <i>remote-url</i><br> <b>fossil configuration push shun</b> <i>remote-url</i> The two command above will pull or push shunning lists from or to the <i>remote-url</i> indicated and merge the lists on the receiving end. "Admin" privilege on the remote server is required in order to push a shun list. In contrast, the shunning list will be automatically received by default as part of a normal client "pull" operation unless disabled by the "<tt>auto-shun</tt>" setting. Note that the shunning list remains in the repository even after the shunned artifact has been removed. This is to prevent the artifact from being reintroduced into the repository the next time it syncs with another repository that has not shunned the artifact. <h3>Managing the shunning list</h3> The complete shunning list for a repository can be viewed by a user with "admin" privilege on the "/shun" URL of the web interface to Fossil. That URL is accessible under the "Admin" button on the default menu bar. Items can be added to or removed from the shunning list. "Sync" operations are inhibited as soon as the artifact is added to the shunning list, but the content of the artifact is not actually removed from the repository until the next time the repository is rebuilt. When viewing individual artifacts with the web interface, "admin" |
︙ | ︙ |
Changes to www/stats.wiki.
1 2 3 4 5 6 7 8 9 | <title>Fossil Performance</title> <h1 align="center">Performance Statistics</h1> The questions will inevitably arise: How does Fossil perform? Does it use a lot of disk space or bandwidth? Is it scalable? In an attempt to answers these questions, this report looks at several projects that use fossil for configuration management and examines how well they are working. The following table is a summary of the results. | | | < | | | < | | | | | | | < | | | | | | | | | < | | | | | < | | | | | | | < | | | | | | | < | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | <title>Fossil Performance</title> <h1 align="center">Performance Statistics</h1> The questions will inevitably arise: How does Fossil perform? Does it use a lot of disk space or bandwidth? Is it scalable? In an attempt to answers these questions, this report looks at several projects that use fossil for configuration management and examines how well they are working. The following table is a summary of the results. (Last updated on 2015-02-28.) Explanation and analysis follows the table. <table border=1> <tr> <th>Project</th> <th>Number Of Artifacts</th> <th>Number Of Check-ins</th> <th>Project Duration<br>(as of 2015-02-28)</th> <th>Uncompressed Size</th> <th>Repository Size</th> <th>Compression Ratio</th> <th>Clone Bandwidth</th> </tr> <tr align="center"> <td>[http://www.sqlite.org/src/timeline | SQLite] <td>56089 <td>14357 <td>5388 days<br>14.75 years <td>3.4 GB <td>45.5 MB <td>73:1 <td>29.9 MB </tr> <tr align="center"> <td>[http://core.tcl.tk/tcl/timeline | TCL] <td>139662 <td>18125 <td>6183 days<br>16.93 years <td>6.6 GB <td>192.6 MB <td>34:1 <td>117.1 MB </tr> <tr align="center"> <td>[/timeline | Fossil] <td>29937 <td>8238 <td>2779 days<br>7.61 years <td>2.3 GB <td>34.0 MB <td>67:1 <td>21.5 MB </tr> <tr align="center"> <td>[http://www.sqlite.org/slt/timeline | SLT] <td>2278 <td>136 <td>2282 days<br>6.25 years <td>2.0 GB <td>144.7 MB <td>13:1 <td>142.1 MB </tr> <tr align="center"> <td>[http://www.sqlite.org/th3.html | TH3] <td>8729 <td>2406 <td>2347 days<br>6.43 years <td>386 MB <td>15.2 MB <td>25:1 <td>12.6 MB </tr> <tr align="center"> <td>[http://www.sqlite.org/docsrc/timeline | SQLite Docs] <td>5503 <td>1631 <td>2666 days<br>7.30 years <td>194 MB <td>10.9 MB <td>17:1 <td>8.37 MB </tr> </table> <h2>Measured Attributes</h2> In Fossil, every version of every file, every wiki page, every change to |
︙ | ︙ | |||
115 116 117 118 119 120 121 | changed, it still only counts as one check-in. The "Uncompressed Size" is the total size of all the artifacts within the repository assuming they were all uncompressed and stored separately on the disk. Fossil makes use of delta compression between related versions of the same file, and then uses zlib compression on the resulting deltas. The total resulting repository size is shown after the uncompressed | < | < | | | | < < | | > > | | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | changed, it still only counts as one check-in. The "Uncompressed Size" is the total size of all the artifacts within the repository assuming they were all uncompressed and stored separately on the disk. Fossil makes use of delta compression between related versions of the same file, and then uses zlib compression on the resulting deltas. The total resulting repository size is shown after the uncompressed size. On the right end of the table, we show the "Clone Bandwidth". This is the total number of bytes sent from server back to the client. The number of bytes sent from client to server is negligible in comparison. These byte counts include HTTP protocol overhead. In the table and throughout this article, "GB" means gigabytes (10<sup><small>9</small></sup> bytes) not <a href="http://en.wikipedia.org/wiki/Gibibyte">gibibytes</a> (2<sup><small>30</small></sup> bytes). Similarly, "MB" and "KB" means megabytes and kilobytes, not mebibytes and kibibytes. <h2>Analysis And Supplemental Data</h2> Perhaps the two most interesting datapoints in the above table are SQLite and SLT. SQLite is a long-running project with long revision chains. Some of the files in SQLite have been edited over a thousand times. Each of these edits is stored as a delta, and hence the SQLite project gets excellent 73:1 compression. SLT, on the other hand, consists of many large (megabyte-sized) SQL scripts that have one or maybe two edits each. There is very little delta compression occurring and so the overall repository compression ratio is much lower. Note also that quite a bit more bandwidth is required to clone SLT than SQLite. For the first nine years of its development, SQLite was versioned by CVS. The resulting CVS repository measured over 320MB in size. So, the developers were surprised to see that the equivalent Fossil project (the first nine years on SQLite) would clone with only 13MB of bandwidth. The "sync" protocol used by fossil has turned out to be surprisingly efficient. A typical check-in on SQLite might use 3 or 4KB of network bandwidth. For example, the [04eef9522386a59e] check-in used a single HTTP request of 2099 bytes and got back a reply of 1116 bytes. The sync protocol is efficient enough that, once cloned, Fossil can easily be used over a dial-up connection. |
Changes to www/style.wiki.
︙ | ︙ | |||
21 22 23 24 25 26 27 | contain no profanity, obscenity, or innuendo. 16. All C-code conforms to ANSI C-89. 17. All comments and identifiers are in English. 18. The program is single-threaded. Do not use threads. | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | contain no profanity, obscenity, or innuendo. 16. All C-code conforms to ANSI C-89. 17. All comments and identifiers are in English. 18. The program is single-threaded. Do not use threads. (One exception to this is the HTTP server implementation for Windows, which we do not know how to implement without the use of threads.) <b>C preprocessor macros</b>: 20. The purpose of every preprocessor macros is clearly explained in a comment associated with its definition. |
︙ | ︙ | |||
44 45 46 47 48 49 50 | <b>Function header comments</b>: 30. Every function has a header comment describing the purpose and use of the function. 31. Function header comment defines the behavior of the function in | | | 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | <b>Function header comments</b>: 30. Every function has a header comment describing the purpose and use of the function. 31. Function header comment defines the behavior of the function in sufficient detail to allow the function to be re-implemented from scratch without reference to the original code. 32. Functions that perform dynamic memory allocation (either directly or indirectly via subfunctions) say so in their header comments. <b>Function bodies</b>: |
︙ | ︙ |
Changes to www/sync.wiki.
︙ | ︙ | |||
122 123 124 125 126 127 128 | <p>A synchronization request between a client and server consists of one or more HTTP requests as described in the previous section. This section details the "x-fossil" content type.</p> <h3>3.1 Line-oriented Format</h3> <p>The x-fossil content type consists of zero or more "cards". Cards | | | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | <p>A synchronization request between a client and server consists of one or more HTTP requests as described in the previous section. This section details the "x-fossil" content type.</p> <h3>3.1 Line-oriented Format</h3> <p>The x-fossil content type consists of zero or more "cards". Cards are separated by the newline character ("\n"). Leading and trailing whitespace on a card is ignored. Blank cards are ignored.</p> <p>Each card is divided into zero or more space separated tokens. The first token on each card is the operator. Subsequent tokens are arguments. The set of operators understood by servers is slightly different from the operators understood by clients, though the two are very similar.</p> |
︙ | ︙ | |||
157 158 159 160 161 162 163 | checks out, then the client is granted all privileges of the specified user.</p> <p>Privileges are cumulative. There can be multiple successful login cards. The session privileges are the bit-wise OR of the privileges of each individual login.</p> | | | | > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | checks out, then the client is granted all privileges of the specified user.</p> <p>Privileges are cumulative. There can be multiple successful login cards. The session privileges are the bit-wise OR of the privileges of each individual login.</p> <h3>3.3 File and Compressed File Cards</h3> <p>Artifacts are transferred using either "file" cards, or "cfile" cards. (The name "file" card comes from the fact that most artifacts correspond to files, and "cfile" is just a compressed file.) </p> <h4>3.3.1 File Cards</h4> <p>For sync protocols, artifacts are transferred using "file" cards. File cards come in two different formats depending on whether the artifact is sent directly or as a delta from some other artifact.</p> <blockquote> <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br> <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i> </blockquote> <p>File cards are different from all other cards in that they followed by in-line "payload" data. The content of the artifact or the artifact delta consists of the first <i>size</i> bytes of the x-fossil content that immediately follow the newline that terminates the file card. Only file and cfile cards have this characteristic. </p> <p>The first argument of a file card is the ID of the artifact that is being transferred. The artifact ID is the lower-case hexadecimal representation of the SHA1 hash of the artifact. The last argument of the file card is the number of bytes of payload that immediately follow the file card. If the file card has only two arguments, that means the payload is the complete content of the artifact. If the file card has three arguments, then the payload is a delta and second argument is the ID of another artifact that is the source of the delta.</p> <p>File cards are sent in both directions: client to server and server to client. A delta might be sent before the source of the delta, so both client and server should remember deltas and be able to apply them when their source arrives.</p> <h4>3.3.2 Compressed File Cards</h4> <p>A client that sends a clone protocol version "3" or greater will receive artifacts as "cfile" cards while cloning. This card was introduced to improve the speed of the transfer of content by sending the compressed artifact directly from the server database to the client.</p> <p>Compressed File cards are similar to File cards, sharing the same in-line "payload" data characteristics and also the same treatment of direct content or delta content. It comes in two different formats depending on whether the artifact is sent directly or as a delta from some other artifact.</p> <blockquote> <b>cfile</b> <i>artifact-id usize csize</i> <b>\n</b> <i>content</i><br> <b>cfile</b> <i>artifact-id delta-artifact-id usize csize</i> <b>\n</b> <i>content</i><br> </blockquote> <p>The first argument of the cfile card is the ID of the artifact that is being transferred. The artifact ID is the lower-case hexadecimal representation of the SHA1 hash of the artifact. The second argument of the cfile card is the original size in bytes of the artifact. The last argument of the cfile card is the number of compressed bytes of payload that immediately follow the cfile card. If the cfile card has only three arguments, that means the payload is the complete content of the artifact. If the cfile card has four arguments, then the payload is a delta and the second argument is the ID of another artifact that is the source of the delta and the third argument is the original size of the delta artifact.</p> <p>Unlike file cards, cfile cards are only sent in one direction during a clone from server to client for clone protocol version "3" or greater.</p> <h3>3.4 Push and Pull Cards</h3> <p>Among the first cards in a client-to-server message are the push and pull cards. The push card tells the server that the client is pushing content. The pull card tells the server that the client wants to pull content. In the event of a sync, both cards are sent. The format is as follows:</p> <blockquote> <b>push</b> <i>servercode projectcode</i><br> <b>pull</b> <i>servercode projectcode</i> </blockquote> <p>The <i>servercode</i> argument is the repository ID for the client. The <i>projectcode</i> is the identifier of the software project that the client repository contains. The projectcode for the client and server must match in order for the transaction to proceed.</p> <p>The server will also send a push card back to the client during a clone. This is how the client determines what project code to put in the new repository it is constructing.</p> |
︙ | ︙ | |||
230 231 232 233 234 235 236 | two-argument format.</p> <blockquote> <b>clone</b><br> <b>clone</b> <i>protocol-version sequence-number</i> </blockquote> | | | > > > > > > > | | | | | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | two-argument format.</p> <blockquote> <b>clone</b><br> <b>clone</b> <i>protocol-version sequence-number</i> </blockquote> <h4>3.5.1 Protocol 3</h4> <p>The latest clients send a two-argument clone message with a protocol version of "3". (Future versions of Fossil might use larger protocol version numbers.) Version "3" of the protocol enhanced version "2" by introducing the "cfile" card which is intended to speed up clone operations. Instead of sending "file" cards, the server will send "cfile" cards</p> <h4>3.5.2 Protocol 2</h4> <p>The sequence-number sent is the number of artifacts received so far. For the first clone message, the sequence number is 0. The server will respond by sending file cards for some number of artifacts up to the maximum message size. <p>The server will also send a single "clone_seqno" card to the client so that the client can know where the server left off. <blockquote> <b>clone_seqno</b> <i>sequence-number</i> </blockquote> <p>The clone message in subsequent HTTP requests for the same clone operation will use the sequence-number from the clone_seqno of the previous reply.</p> <p>In response to an initial clone message, the server also sends the client a push message so that the client can discover the projectcode for this project.</p> <h4>3.5.3 Legacy Protocol</h4> <p>Older clients send a clone card with no argument. The server responds to a blank clone card by sending an "igot" card for every artifact in the repository. The client will then issue "gimme" cards to pull down all the content it needs. <p>The legacy protocol works well for smaller repositories (50MB with 50,000 |
︙ | ︙ | |||
336 337 338 339 340 341 342 | <p>The reqconfig card is normally sent in response to the "fossil configuration pull" command. The format is as follows: <blockquote> <b>reqconfig</b> <i>configuration-name</i> </blockquote> | | > > > > > > > > > > > > > | > > | | > > > > > > < | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 | <p>The reqconfig card is normally sent in response to the "fossil configuration pull" command. The format is as follows: <blockquote> <b>reqconfig</b> <i>configuration-name</i> </blockquote> <p>As of [/timeline?r=trunk&c=2015-03-19+03%3A57%3A46&n=20|2015-03-19], the configuration-name must be one of the following values: <center><table border=0> <tr><td valign="top"> <ul> <li> css <li> header <li> footer <li> logo-mimetype <li> logo-image <li> background-mimetype <li> background-image <li> index-page <li> timeline-block-markup <li> timeline-max-comment <li> timeline-plaintext <ul></td><td valign="top"><ul> <li> adunit <li> adunit-omit-if-admin <li> adunit-omit-if-user <li> white-foreground <li> project-name <li> short-project-name <li> project-description <li> index-page <li> manifest <li> binary-glob <li> clean-glob <ul></td><td valign="top"><ul> <li> ignore-glob <li> keep-glob <li> crnl-glob <li> encoding-glob <li> empty-dirs <li> allow-symlinks <li> dotfiles <li> ticket-table <li> ticket-common <li> ticket-change <li> ticket-newpage <ul></td><td valign="top"><ul> <li> ticket-viewpage <li> ticket-editpage <li> ticket-reportlist <li> ticket-report-template <li> ticket-key-template <li> ticket-title-expr <li> ticket-closed-expr <li> @reportfmt <li> @user <li> @concealed <li> @shun |
︙ | ︙ | |||
443 444 445 446 447 448 449 | <h3>3.13 Unknown Cards</h3> <p>If either the client or the server sees a card that is not described above, then it generates an error and aborts.</p> <h2>4.0 Phantoms And Clusters</h2> | | | | | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 | <h3>3.13 Unknown Cards</h3> <p>If either the client or the server sees a card that is not described above, then it generates an error and aborts.</p> <h2>4.0 Phantoms And Clusters</h2> <p>When a repository knows that an artifact exists and knows the ID of that artifact, but it does not know the artifact content, then it stores that artifact as a "phantom". A repository will typically create a phantom when it receives an igot card for an artifact that it does not hold or when it receives a file card that references a delta source that it does not hold. When a server is generating its reply or when a client is generating a new request, it will usually send gimme cards for every phantom that it holds.</p> <p>A cluster is a special artifact that tells of the existence of other artifacts. Any artifact in the repository that follows the syntactic rules of a cluster is considered a cluster.</p> <p>A cluster is line oriented. Each line of a cluster is a card. The cards are separated by the newline ("\n") character. Each card consists of a single character card type, a space, and a single argument. No extra whitespace and no trailing or leading whitespace is allowed. All cards in the cluster must occur in strict lexicographical order.</p> <p>A cluster consists of one or more "M" cards followed by a single "Z" card. Each M card holds an argument which is an artifact ID for an artifact in the repository. The Z card has a single argument which is the lower-case hexadecimal representation of the MD5 checksum of all preceding M cards up to and included the newline character that occurred just before the Z that starts the Z card.</p> <p>Any artifact that does not match the specifications of a cluster exactly is not a cluster. There must be no extra whitespace in |
︙ | ︙ |
Changes to www/tech_overview.wiki.
︙ | ︙ | |||
122 123 124 125 126 127 128 | a way to change settings for all repositories with a single command, rather than having to change the setting individually on each repository. The configuration database also maintains a list of repositories. This list is used by the [/help/all | fossil all] command in order to run various operations such as "sync" or "rebuild" on all repositories managed by a user. | | | > > > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | a way to change settings for all repositories with a single command, rather than having to change the setting individually on each repository. The configuration database also maintains a list of repositories. This list is used by the [/help/all | fossil all] command in order to run various operations such as "sync" or "rebuild" on all repositories managed by a user. On Unix systems, the configuration database is named ".fossil" and is located in the user's home directory. On Windows, the configuration database is named "_fossil" (using an underscore as the first character instead of a dot) and is located in the directory specified by the LOCALAPPDATA, APPDATA, or HOMEPATH environment variables, in that order. You can override this default location by defining the environment variable FOSSIL_HOME pointing to an appropriate (writable) directory. <h3>2.2 Repository Databases</h3> The repository database is the file that is commonly referred to as "the repository". This is because the repository database contains, among other things, the complete revision, ticket, and wiki history for a project. It is customary to name the repository database after then name of the project, with a ".fossil" suffix. For example, the repository |
︙ | ︙ | |||
184 185 186 187 188 189 190 | <h4>2.2.2 Project Metadata</h4> The global project state information in the repository database is supplemented by computed metadata that makes querying the project state more efficient. Metadata includes information such as the following: | | | | | | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | <h4>2.2.2 Project Metadata</h4> The global project state information in the repository database is supplemented by computed metadata that makes querying the project state more efficient. Metadata includes information such as the following: * The names for all files found in any check-in. * All check-ins that modify a given file * Parents and children of each check-in. * Potential timeline rows. * The names of all symbolic tags and the check-ins they apply to. * The names of all wiki pages and the artifacts that comprise each wiki page. * Attachments and the wiki pages or tickets they apply to. * Current content of each ticket. * Cross-references between tickets, check-ins, and wiki pages. The metadata is held in various SQL tables in the repository database. The metadata is designed to facilitate queries for the various timelines and reports that Fossil generates. As the functionality of Fossil evolves, the schema for the metadata can and does change. But schema changes do no invalidate the repository. Remember that the |
︙ | ︙ | |||
297 298 299 300 301 302 303 | * The name of the repository database file. * The version that is currently checked out. * Files that have been [/help/add | added], [/help/rm | removed], or [/help/mv | renamed] but not yet committed. * The mtime and size of files as they were originally checked out, in order to expedite checking which files have been edited. | | | 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | * The name of the repository database file. * The version that is currently checked out. * Files that have been [/help/add | added], [/help/rm | removed], or [/help/mv | renamed] but not yet committed. * The mtime and size of files as they were originally checked out, in order to expedite checking which files have been edited. * Other check-ins that have been [/help/merge | merged] into the working checkout but not yet committed. * Copies of files prior to the most recent undoable operation - needed to implement the [/help/undo | undo] and [/help/redo | redo] commands. * The [/help/stash | stash]. * State information for the [/help/bisect | bisect] command. For Fossil commands that run from within a working checkout, the |
︙ | ︙ |
Added www/th1.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | TH1 Scripts =========== TH1 is a very small scripting language used to help generate web-page content in Fossil. Origins ------- TH1 began as a minimalist re-implementation of the TCL scripting language. There was a need to test the SQLite library on Symbian phones, but at that time all of the test cases for SQLite were written in Tcl and Tcl could not be easily compiled on the SymbianOS. So TH1 was developed as a cut-down version of TCL that would facilitate running the SQLite test scripts on SymbianOS. The testing of SQLite on SymbianOS was eventually accomplished by other means. But Fossil was first being designed at about the same time. Early prototypes of Fossil were written in pure TCL. But as the development shifted toward the use of C-code, the need arose to have a TCL-like scripting language to help with code generation. TH1 was small and light-weight and used minimal resources and seemed ideally suited for the task. The name "TH1" stands "Test Harness 1", since that was its original purpose. Overview -------- TH1 is a string-processing language. All values are strings. Any numerical operations are accomplished by converting from string to numeric, performing the computation, then converting the result back into a string. (This might seem inefficient, but it is faster than people imagine, and numeric computations do not come up very often for the kinds of work that TH1 does, so it has never been a factor.) A TH1 script consist of a sequence of commands. Each command is terminated by the first (unescaped) newline or ";" character. The text of the command (excluding the newline or semicolon terminator) is broken into space-separated tokens. The first token is the command name and subsequent tokens are the arguments. In this since, TH1 syntax is similar to the familiar command-line shell syntax. A token is any sequence of characters other than whitespace and semicolons. Or, all text without double-quotes is a single token even if it includes whitespace and semicolons. Or, all text without nested {...} pairs is a single token. The nested {...} form of tokens is important because it allows TH1 commands to have an appearance similar to C/C++. It is important to remember, though, that a TH1 script is really just a list of text commands, not a context-free language with a grammar like C/C++. This can be confusing to long-time C/C++ programmers because TH1 does look a lot like C/C++. But the semantics of TH1 are closer to FORTH or Lisp than they are to C. Consider the "if" command in TH1. if {$current eq "dev"} { puts "hello" } else { puts "world" } The example above is a single command. The first token, and the name of the command, is "if". The second token is '$current eq "dev"' - an expression. (The outer {...} are removed from each token by the command parser.) The third token is the 'puts "hello"', with its whitespace and newlines. The fourth token is "else". And the fifth and last token is 'puts "world"'. The "if" command word by evaluating its first argument (the second token) as an expression, and if that expression is true, evaluating its second argument (the third token) as a TH1 script. If the expression is false and the third argument is "else" then the fourth argument is evaluated as a TH1 expression. So, you see, even though the example above spans five lines, it is really just a single command. Summary of Core TH1 Commands ---------------------------- The original TCL language after when TH1 is modeled has a very rich repertoire of commands. TH1, as it is designed to be minimalist and embedded has a greatly reduced command set. The following bullets summarize the commands available in TH1: * break * catch SCRIPT ?VARIABLE? * continue * error ?STRING? * expr EXPR * for INIT-SCRIPT TEST-EXPR NEXT-SCRIPT BODY-SCRIPT * if EXPR SCRIPT (elseif EXPR SCRIPT)* ?else SCRIPT? * info exists VARNAME * lindex LIST INDEX * list ARG ... * llength LIST * proc NAME ARG-LIST BODY-SCRIPT * rename OLD NEW * return ?-code CODE? ?VALUE? * set VARNAME VALUE * string compare STR1 STR2 * string first NEEDLE HAYSTACK ?START-INDEX? * string is CLASS STRING * string last NEEDLE HAYSTACK ?START-INDEX? * string length STRING * string range STRING FIRST LAST * string repeat STRING COUNT * unset VARNAME * uplevel ?LEVEL? SCRIPT * upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR? All of the above commands works as in the original TCL. Refer to the TCL documentation for details. TH1 Extended Commands --------------------- There are many new commands added to TH1 and used to access the special features of Fossil. The following is a summary of the extended commands: * anoncap * anycap * artifact * checkout * combobox * date * decorate * enable_output * getParameter * globalState * httpize * hascap * hasfeature * html * htmlize * http * linecount * puts * query * randhex * regexp * reinitialize * render * repository * searchable * setParameter * setting * styleHeader * styleFooter * tclReady * trace * stime * utime * wiki Each of the commands above is documented by a block comment above their implementation in the th_main.c source file. **To Do:** We would like to have a community volunteer go through and copy the documentation for each of these commands (with appropriate format changes and spelling and grammar corrections) into subsequent sections of this document. It is suggested that the list of extension commands be left intact - as a quick reference. But it would be really nice to also have the details of what each command does. |
Changes to www/tickets.wiki.
︙ | ︙ | |||
99 100 101 102 103 104 105 | The <b>tkt_id</b> fields of TICKET and TICKETCHNG are an integer key used to uniquely identify the ticket to which the row belongs. These keys are for internal use only and may change when doing a "fossil rebuild". The <b>tkt_uuid</b> field is the unique hexadecimal identifier for the ticket. Ticket identifiers appear to be SHA1 hash strings, but they | | | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | The <b>tkt_id</b> fields of TICKET and TICKETCHNG are an integer key used to uniquely identify the ticket to which the row belongs. These keys are for internal use only and may change when doing a "fossil rebuild". The <b>tkt_uuid</b> field is the unique hexadecimal identifier for the ticket. Ticket identifiers appear to be SHA1 hash strings, but they are not really the hash of any identifiable artifact. They are just random hexadecimal numbers. When creating a new ticket, Fossil uses a (high-quality) pseudo-random number generator to create the ticket number. The ticket numbers are large so that the chance of collision between any two tickets is vanishingly small. The <b>tkt_mtime</b> field of TICKET shows the time (as a Julian day number) of the most recent ticket change artifact for that ticket. The <b>tkt_mtime</b> field of TICKETCHNG shows the timestamp on the ticket change artifact that the TICKETCHNG row refers to. The <b>tkt_ctime</b> field of TICKET is the time of the oldest ticket change artifact for that ticket, thus holding the time that the ticket was created. The <b>tkt_rid</b> field of TICKETCHNG is the integer primary key in the BLOB table of the ticket change artifact that gave rise to the row in the TICKETCHNG table. |
︙ | ︙ | |||
158 159 160 161 162 163 164 | To reconstruct the TICKET table, the ticket change artifacts are visited in timestamp order. As each ticket change artifact is visited, its key/value pairs are examined. For any key/value pair in which the key is the same as a field in the TICKET table, the value of that pair either replaces or is appended to the previous value of the corresponding field in the TICKET table. Whether a value is replaced or appended is determined by markings in the ticket change | | | | | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | To reconstruct the TICKET table, the ticket change artifacts are visited in timestamp order. As each ticket change artifact is visited, its key/value pairs are examined. For any key/value pair in which the key is the same as a field in the TICKET table, the value of that pair either replaces or is appended to the previous value of the corresponding field in the TICKET table. Whether a value is replaced or appended is determined by markings in the ticket change artifact itself. Most fields are usually replaced. (For example, to change the status from "Open" to "Fixed" would involve a key value pair "status/Fixed" with the replace attribute set). The main exception is the "comment" field, which is usually appended with new comment text. Note that the replace-or-append mark on ticket change artifacts is only used by the TICKET table. Since the initial value of all fields in the TICKETCHNG table is NULL, the replace-or-append mark makes no difference there. <h3>2.3 Old-Style versus New-Style Tickets</h3> Older versions of Fossil (before [/timeline?c=2012-11-27T16:26:29 | 2012-11-27]) only supported the TICKET table. In this older style, new comments were added to tickets by using the append-value feature on the comment field. Thus the TICKET.COMMENT field contains the complete text of all user comments already appended together and ready for display. A problem with the old approach is that all comment text had to be in the same format. In other words, the all comment text had to be either plaintext or wiki or HTML. It was not possible for some comments to be in HTML and others to be plaintext. Some site administrators wanted the ability to mix plaintext, wiki, and HTML comments and display each comment according to its chosen format. Hence, Fossil was enhanced to support the "new-style" tickets. The TICKETCHNG table was added to support new-style tickets. In the new style, comment text is stored with the "icomment" (for "Incremental Comment") key and appears separately, and with its on mimetype, in multiple rows of the TICKETCHNG table. It then falls to the TH1 script code on the View Ticket Page to query the TICKETCHNG table and extract and format the various comments in timestamp order. |
Added www/webpage-ex.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | Web-Page Examples ================= Here are a few examples of the many web pages supported by Fossil. This is not an exhaustive list. Explore hyperlinks to see more. <style> .exbtn { border: 1px solid #000; margin: 1ex; border-radius: 1ex; padding: 0 1ex; background-color: #eee; } </style> * <a target='_blank' class='exbtn' href='../../../timeline?y=ci&n=100'>Example</a> 100 most recent check-ins. * <a target='_blank' class='exbtn' href='../../../finfo?name=src/file.c'>Example</a> All changes to the <b>src/file.c</b> source file. * <a target='_blank' class='exbtn' href='../../../timeline?n=200&uf=0c3c2d086a'>Example</a> All check-ins using a particular version of the <b>src/file.c</b> source file. * <a target='_blank' class='exbtn' href='../../../timeline?n=11&y=ci&c=2014-01-01'>Example</a> Check-ins proximate to an historical point in time (2014-01-01). * <a target='_blank' class='exbtn' href='../../../timeline?n=11&y=ci&c=2014-01-01&v=1'>Example</a> The previous augmented with file changes. * <a target='_blank' class='exbtn' href='../../../timeline?n=25&y=ci&a=1970-01-01'>Example</a> First 25 check-ins after 1970-01-01. (The first 25 check-ins of the project.) * <a target='_blank' class='exbtn' href='../../../timeline?n=200&r=svn-import'>Example</a> All check-ins of the "svn-import" branch together with check-ins that merge with that branch. * <a target='_blank' class='exbtn' href='../../../timeline?n=200&t=svn-import'>Example</a> All check-ins of the "svn-import" branch only. * <a target='_blank' class='exbtn' href='../../../timeline?n=100&y=ci&ubg'>Example</a> 100 most recent check-ins color coded by committer. * <a target='_blank' class='exbtn' href='../../../timeline?from=version-1.27&to=version-1.28'>Example</a> All check-ins on the most direct path from version-1.27 to version-1.28 (Hint: In any graph above, click the square node boxes for two check-ins or files to see a diff.) * <a target='_blank' class='exbtn' href='../../../tree?ci=daff9d20621&type=tree'>Example</a> All files for a particular check-in (daff9d20621480) * <a target='_blank' class='exbtn' href='../../../tree?ci=trunk&type=tree&mtime=1'>Example</a> All files for the latest check-in on a branch (trunk) sorted by last modification time. * <a target='_blank' class='exbtn' href='../../../fileage?name=svn-import'>Example</a> Age of all files in the latest checking for branch "svn-import". * <a target='_blank' class='exbtn' href='../../../brlist'>Example</a> Table of branches. (Click on column headers to sort.) * <a target='_blank' class='exbtn' href='../../../stat'>Example</a> Overall repository status. * <a target='_blank' class='exbtn' href='../../../reports?view=byfile'>Example</a> Number of check-ins for each source file. (Click on column headers to sort.) * <a target='_blank' class='exbtn' href='../../../blame?checkin=5260fbf63287&filename=src/rss.c&limit=-1'> Example</a> Most recent change to each line of a particular source file in a particular check-in. * <a target='_blank' class='exbtn' href='../../../taglist'>Example</a> List of tags on check-ins. |
Changes to www/webui.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 | <title>The Fossil Web Interface</title> One of the innovative features of Fossil is its built-in web interface. This web interface provides everything you need to run a software development project: * [./bugtheory.wiki | Ticketing and bug tracking] * [./wikitheory.wiki | Wiki] * [./embeddeddoc.wiki | On-line documentation] * Status information * Timelines * Graphs of revision and branching history | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <title>The Fossil Web Interface</title> One of the innovative features of Fossil is its built-in web interface. This web interface provides everything you need to run a software development project: * [./bugtheory.wiki | Ticketing and bug tracking] * [./wikitheory.wiki | Wiki] * [./embeddeddoc.wiki | On-line documentation] * Status information * Timelines * Graphs of revision and branching history * [./event.wiki | Technical notes] * File and version lists and differences * Download historical versions as ZIP archives * Historical change data * Add and remove tags on check-ins * Move check-ins between branches * Revise check-in comments * Manage user credentials and access permissions * And so forth... (some [./webpage-ex.md|examples]) You get all of this, and more, for free when you use Fossil. There are no extra programs to install or setup. Everything you need is already pre-configured and built into the self-contained, stand-alone Fossil executable. As an example of how useful this web interface can be, |
︙ | ︙ | |||
102 103 104 105 106 107 108 | menu bar. Fossil uses simple and easy-to-remember [/wiki_rules | wiki formatting rules] so you won't have to spend a lot of time learning a new markup language. And, as with tickets, all of your edits will automatically merge with those of your co-workers when your repository synchronizes. You can view summary reports of <b>branches</b> in the | | | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | menu bar. Fossil uses simple and easy-to-remember [/wiki_rules | wiki formatting rules] so you won't have to spend a lot of time learning a new markup language. And, as with tickets, all of your edits will automatically merge with those of your co-workers when your repository synchronizes. You can view summary reports of <b>branches</b> in the check-in graph by visiting the "Branches" link on the menu bar. From those pages you can follow hyperlinks to get additional details. These screens allow you to easily keep track of what is going on with separate subteams within your project team. The "Files" link on the menu allows you to browse through the <b>file hierarchy</b> of the project and to view complete changes histories on individual files, with hyperlinks to the check-ins that made those changes, and with diffs and annotated diffs between versions. The web interface supports [./embeddeddoc.wiki | embedded documentation]. Embedded documentation is documentation files (usually in wiki format) that are checked into project as part of the source tree. Such files |
︙ | ︙ |
Changes to www/wikitheory.wiki.
1 2 3 4 5 6 7 8 9 | <title>Wiki In Fossil</title> <h2>Introduction</h2> Fossil uses [/wiki_rules | wiki markup] for many things: * Stand-alone wiki pages. * Description and comments in [./bugtheory.wiki | bug reports]. * Check-in comments. * [./embeddeddoc.wiki | Embedded documentation] files whose | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <title>Wiki In Fossil</title> <h2>Introduction</h2> Fossil uses [/wiki_rules | wiki markup] for many things: * Stand-alone wiki pages. * Description and comments in [./bugtheory.wiki | bug reports]. * Check-in comments. * [./embeddeddoc.wiki | Embedded documentation] files whose name ends in ".wiki". * [./event.wiki | Technical notes]. The [/wiki_rules | formatting rules] for fossil wiki are designed to be simple and intuitive. The idea is that wiki provides paragraph breaks, numbered and bulleted lists, and hyperlinking for simple documents together with a safe subset of HTML for more complex formatting tasks. |
︙ | ︙ | |||
29 30 31 32 33 34 35 36 37 38 39 40 41 42 | all formatting tasks. 3. Where the fossil wiki markup language is insufficient, HTML is used. HTML is a standard language familiar to most programmers so there is nothing new to learn. And, though cumbersome, the HTML does not need to be used very often so is not a burden. <h2>Stand-alone Wiki Pages</h2> Each wiki page has its own revision history which is independent of the sequence of check-ins (check-ins). Wiki pages can branch and merge just like check-ins, though as of this writing (2008-07-29) there is no mechanism in the user interface to support branching and merging. | > > > > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | all formatting tasks. 3. Where the fossil wiki markup language is insufficient, HTML is used. HTML is a standard language familiar to most programmers so there is nothing new to learn. And, though cumbersome, the HTML does not need to be used very often so is not a burden. UPDATE: Since 2012, Fossil also contains a [/md_rules | Markdown] rendering engine. Markdown can optionally be used to format [./embeddeddoc.wiki | embedded documents], wiki pages, [./event.wiki | technical notes], and bug report text. <h2>Stand-alone Wiki Pages</h2> Each wiki page has its own revision history which is independent of the sequence of check-ins (check-ins). Wiki pages can branch and merge just like check-ins, though as of this writing (2008-07-29) there is no mechanism in the user interface to support branching and merging. |
︙ | ︙ |